Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

DailyCode

[SOLID] 단일 책임 원칙(SRP) 본문

디자인 패턴

[SOLID] 단일 책임 원칙(SRP)

JSKoder 2023. 3. 16. 22:50

SOLID 원칙 : 로버트 마틴이 개발한 객체 지향 프로그래밍의 다섯 가지 기본 원칙인 SOLID 원칙은 소프트웨어를 구축하며 확장성과 유지 관리를 쉽게 할 수 있는 지침입니다.

마이클 페더스가 이 원칙들을 이해하기 쉽게 두 문자어 기억술을 사용하여 SOLID라는 이름으로 소개했습니다.

이 글에서는 SOLID 원칙 중 'S', 즉 단일 책임 원칙(Single Responsibility Principle, SRP)에 대해 알아보겠습니다.

SRP(Single Responsibility Principle): 단일 책임 원칙

정의

  • 단일 책임 원칙은 소프트웨어 시스템의 각 클래스 또는 모듈이 하나의 책임을 가지며, 해당 책임만을 수행해야 함을 강조하는 원칙입니다.
  • 각 클래스는 하나의 책임 또는 기능을 캡슐화하며, 해당 기능의 변경이 시스템의 다른 부분에 영향을 미치지 않아야 합니다.
  • 소프트웨어 개발자는 각 구성 요소가 문제 도메인의 단일 측면에 초점을 맞추기 때문에 보다 유지 관리가 용이하고 확장 가능한 시스템을 만들 수 있습니다.

적용 방법

  1. 시스템의 책임 또는 기능을 식별합니다.
  2. 이러한 책임을 각각 별개의 클래스 또는 모듈로 분리합니다.
  3. 각 클래스 또는 모듈이 지정된 책임에만 집중하도록 합니다.
  4. 클래스 또는 모듈 간의 상호 작용은 각각의 책임으로 제한합니다.

장점

  • 유지보수성 향상: 각 클래스가 단일 책임에 집중하기 때문에 시스템의 한 측면을 변경해도 관련 없는 다른 구성 요소에 영향을 미칠 가능성이 적습니다.
  • 테스트 가능성 향상: 단일 책임이 있는 집중형 클래스는 다른 컴포넌트와의 종속성 및 상호 작용이 적기 때문에 테스트하기가 더 쉽습니다.
  • 유연성 및 확장성 향상: 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))

개선 소스는 다음과같은 이점을 얻을 수 있습니다.

  1. 높은 응집력: 클래스의 역할이 명확하게 분리되어 클래스 내부의 메소드와 속성들이 서로 밀접하게 연관되어 있습니다.
  2. 코드 재사용성 향상: 급여 보너스 계산 로직이 PayrollCalculator 클래스로 이동함에 따라, 로직을 재사용하기가 쉬워졌습니다. 이제 다른 종류의 직원이 추가되더라도, PayrollCalculator 클래스의 인스턴스를 사용해 급여 보너스 계산을 있습니다.
  3. 낮은 결합도: Employee 클래스와 PayrollCalculator 클래스가 분리되어 결합도가 낮아졌습니다. 이로 인해 클래스의 변경이 다른 클래스에 미치는 영향이 줄어들어 코드의 안정성이 향상됩니다.

결론

단일 책임 원칙은 소프트웨어 시스템의 유지보수성, 테스트 가능성 및 확장성을 촉진하는 SOLID 설계 원칙의 중요한 측면입니다.

시스템의 각 클래스 또는 모듈이 단일 책임에 집중하도록 함으로써 개발자는 변화하는 요구 사항에 쉽게 적응할 수 있는 보다 견고하고 유연한 시스템을 만들 수 있습니다.