Notice
Recent Posts
Recent Comments
Link
DailyCode
[SOLID] 단일 책임 원칙(SRP) 본문
SOLID 원칙 : 로버트 마틴이 개발한 객체 지향 프로그래밍의 다섯 가지 기본 원칙인 SOLID 원칙은 소프트웨어를 구축하며 확장성과 유지 관리를 쉽게 할 수 있는 지침입니다.
마이클 페더스가 이 원칙들을 이해하기 쉽게 두 문자어 기억술을 사용하여 SOLID라는 이름으로 소개했습니다.
이 글에서는 SOLID 원칙 중 'S', 즉 단일 책임 원칙(Single Responsibility Principle, SRP)에 대해 알아보겠습니다.
SRP(Single Responsibility Principle): 단일 책임 원칙
정의
- 단일 책임 원칙은 소프트웨어 시스템의 각 클래스 또는 모듈이 하나의 책임을 가지며, 해당 책임만을 수행해야 함을 강조하는 원칙입니다.
- 각 클래스는 하나의 책임 또는 기능을 캡슐화하며, 해당 기능의 변경이 시스템의 다른 부분에 영향을 미치지 않아야 합니다.
- 소프트웨어 개발자는 각 구성 요소가 문제 도메인의 단일 측면에 초점을 맞추기 때문에 보다 유지 관리가 용이하고 확장 가능한 시스템을 만들 수 있습니다.
적용 방법
- 시스템의 책임 또는 기능을 식별합니다.
- 이러한 책임을 각각 별개의 클래스 또는 모듈로 분리합니다.
- 각 클래스 또는 모듈이 지정된 책임에만 집중하도록 합니다.
- 클래스 또는 모듈 간의 상호 작용은 각각의 책임으로 제한합니다.
장점
- 유지보수성 향상: 각 클래스가 단일 책임에 집중하기 때문에 시스템의 한 측면을 변경해도 관련 없는 다른 구성 요소에 영향을 미칠 가능성이 적습니다.
- 테스트 가능성 향상: 단일 책임이 있는 집중형 클래스는 다른 컴포넌트와의 종속성 및 상호 작용이 적기 때문에 테스트하기가 더 쉽습니다.
- 유연성 및 확장성 향상: SRP를 준수하면 모듈화되고 체계적인 시스템을 구축할 수 있으므로 기능을 추가하거나 수정하기가 용이합니다.
SRP 원칙 미적용 소스
//BAD
class Employee {
let name: String
let startDate: Date
let salary: CGFloat
init(name: String, startDate: Date, salary: CGFloat) {
self.name = name
self.startDate = startDate
self.salary = salary
}
func generateEmployeePayment() -> String {
// 연봉 계산 로직
let payStub = "name: \\(name), startDate: \\(startDate), salary: \\(salary)"
return payStub
}
//TODO: BAD case
func calculateBonus() -> Double {
return salary * 0.2
}
}
Employee 클래스는 두 가지 책임을 가지고 있어서 단일 책임 원칙을 위배합니다
문제점
- 직원 정보를 저장하는 클래스가 증가할 경우, 급여 및 보너스 계산 로직을 다시 구현하거나 Employee 클래스 인스턴스를 생성하여 사용해야 합니다. 이러한 접근 방식은 코드의 재사용성이 낮아집니다.
- 클래스 내부의 응집력이 낮아 다음과 같은 문제가 발생합니다.
- 응집력이 낮은 클래스는 이해하기 어렵고, 재사용하기도 어렵습니다. 따라서 유지보수가 어렵고, 다른 클래스의 변화에 민감하게 반응할 수 있습니다.
개선소스
// 직원 정보 저장
class Employee {
let name: String
let startDate: Date
let salary: CGFloat
init(name: String, startDate: Date, salary: CGFloat) {
self.name = name
self.startDate = startDate
self.salary = salary
}
}
// 급여 및 보너스 계산
class PayrollCalculator {
func generateEmployeePayment(for employee: Employee) -> String {
// 연봉 계산 로직
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
let formattedStartDate = dateFormatter.string(from: employee.startDate)
let payStub = "name: \\(employee.name), startDate: \\(formattedStartDate), salary: \\(employee.salary)"
return payStub
}
func calculateBonus(for employee: Employee) -> Double {
return employee.salary * 0.2
}
}
let jsKoder = Employee(name: "JsKoder", startDate: Date.now, salary: 200000)
let calculator = PayrollCalculator()
print(calculator.generateEmployeePayment(for: jsKoder))
print(calculator.calculateBonus(for: jsKoder))
개선 소스는 다음과같은 이점을 얻을 수 있습니다.
- 높은 응집력: 각 클래스의 역할이 명확하게 분리되어 클래스 내부의 메소드와 속성들이 서로 밀접하게 연관되어 있습니다.
- 코드 재사용성 향상: 급여 및 보너스 계산 로직이 PayrollCalculator 클래스로 이동함에 따라, 이 로직을 재사용하기가 더 쉬워졌습니다. 이제 다른 종류의 직원이 추가되더라도, PayrollCalculator 클래스의 인스턴스를 사용해 급여 및 보너스 계산을 할 수 있습니다.
- 낮은 결합도: Employee 클래스와 PayrollCalculator 클래스가 분리되어 결합도가 낮아졌습니다. 이로 인해 한 클래스의 변경이 다른 클래스에 미치는 영향이 줄어들어 코드의 안정성이 향상됩니다.
결론
단일 책임 원칙은 소프트웨어 시스템의 유지보수성, 테스트 가능성 및 확장성을 촉진하는 SOLID 설계 원칙의 중요한 측면입니다.
시스템의 각 클래스 또는 모듈이 단일 책임에 집중하도록 함으로써 개발자는 변화하는 요구 사항에 쉽게 적응할 수 있는 보다 견고하고 유연한 시스템을 만들 수 있습니다.
'디자인 패턴' 카테고리의 다른 글
[SOLID] 리스코프 치환 법칙(LSP) (0) | 2023.03.25 |
---|---|
[SOLID] 개방 폐쇄 원칙(OCP) (0) | 2023.03.24 |
템플릿 메서드 패턴(TemplateMethod Pattern) (0) | 2023.03.09 |
스트레티지 패턴(Strategy Pattern) (0) | 2023.03.08 |
커맨드 패턴(Command Pattern) (0) | 2023.03.07 |