[Clean Architecture] CH7 SRP(단일 책임 원칙)

1 minute read


SRP(단일 책임 원칙)

  • 모든 모듈은 단 하나의 일만 해야한다는 뜻이 아님
    • 이는 오히려 함수가 반드시 하나의, 단 하나의 일만 해야 한다는 원칙에 가까움
  • 하나의 모듈은 하나의, 오직 하나의 액터에 대해서만 책임져야 함
    • 모듈: 소스 파일 혹은 함수와 데이터 구조로 구성된 응집된 집합

징후1: 우발적 중복

class Employee:
    def calculatePay:
        rh = self.regularHours()
        pass

    def reportHours:
        rh = self.regularHours()
        pass

    def save:
        pass

    def regularHours:
        pass
  • 이 클래스는 세 가지 메서드가 매우 다른 세명의 액터를 책임지기 때문에 SRP를 위반함
    • calculatePay: 회계팀
    • reportHours: 인사팀
    • save: 개발팀
  • 만약 regularHours와 같이 액터가 다르지만 두 함수가 동시에 호출하는 메소드가 존재하고, 한 액터에서 이 함수를 수정하면, 다른 액터에게까지 영향이 미침

징후2: 병합

  • regularHours를 동시에 다른 액터들이 수정한다면 병합이 발생할 확률이 높음
  • 따라서 서로 다른 액터를 뒷받침하는 코드를 서로 분리하는 것이 좋음

해결책

  • 해결책은 다양하지만, 그 모두가 메서드를 각기 다른 클래스로 이동시키는 방식임
  • 한 가지 해결 방법은 아래와 같음
class EmployeeData:
    pass

class PayCalculator:
    def calculatePay:
        pass

class HourReporter:
    def reportHours:
        pass

class EmployeeSaver:
    def saveEmployee:
        pass
  • 각 클래스는 자신의 메서드에 반드시 필요한 소스 코드만을 포함하고 세 클래스는 서로의 존재를 몰라 ‘우연한 중복’을 피할 수 있음
  • 다만, 세 가지 클래스를 인스턴스화하고 추적해야한다는 단점이 존재
  • 이를 해결하기 위해 흔히 쓰는 패턴으로는 퍼사드(Facade) 패턴이 존재
class EmployeeData:
    pass

class EmployeeFacade:
    def calculatePay:
        PayCalculator.calculatePay()

    def reportHours:
        HourReporter.reportHours()

    def save:
        EmployeeSaver.saveEmployee()

class PayCalculator:
    def calculatePay:
        pass

class HourReporter:
    def reportHours:
        pass

class EmployeeSaver:
    def saveEmployee:
        pass
  • EmployeeFacade 클래스는 세 클래스의 객체를 생성, 요청된 메서드를 가지는 객체로 위임하는 일을 책임짐
  • 어떤 개발자는 중요한 업무 규칙을 데이터와 가깝게 배치하는 방식을 선호함
    • 이 경우 가장 중요한 메서드는 Employee 클래스에 그대로 유지하되, Employee 클래스를 덜 중요한 나머지 메서드들에 대한 퍼사드로 사용할 수 있음
class Employee:
    def calculatePay:
        pass

    def reportHours:
        HourReporter.reportHours()

    def save:
        EmployeeSaver.saveEmployee()

class HourReporter:
    def reportHours:
        pass

class EmployeeSaver:
    def saveEmployee:
        pass