SRP: 단일 책임 원칙

  • Single Responsibility Principle
  • 이름만 보면 하나의 모듈은 하나의 일만 해야한다로 해석될 수 있지만 이는 잘못된 해석
    • 단 하나의 일만 해야하는것은 함수, 이는 SOLID도 아니고 SRP도 아님
  • 역사적으로 SRP는 아래와 같이 기술됨

단일 모듈은 변경의 이유가 하나, 오직 하나뿐이어야 한다.

  • 소프트웨어 시스템은 사용자이해관계자를 만족시키기 위해 변경됨
    • SRP가 말하는 “변경의 이유”란 사용자와 이해관계자를 의미
  • 위를 적용하면 SRP를 아래과 같이 표현 가능

하나의 모듈은 하나의, 오직 하나의 사용자 또는 이해관계자에 대해서만 책임저야 한다.

  • 여기서 사용자와 이해관계자라는 표현을 사용하는것은 적합하지 않음
    • 동일한 방식으로 변경되지를 원하는게 두 명이상 일수도 있기 때문
    • 변경을 요청하는 집단을 액터라고 부름
  • SRP의 최종 버전은 아래와 같음

하나의 모듈은 하나의, 오직 하나의 액터에 대해서만 책임져야 한다.

  • 모듈의 가장 단순한 정의는 소스 파일
    • 모듈은 함수와 데이터구조로 구성된 응집된 집합
  • 단일 액터를 책임지는 코드를 묶어주는 힘이 바로 응집성(cohesion)

징후 1: 우발적 중복

  • SRP를 위반하는 Employee라는 클래스가 존재
    • 세가지 메서드가 존재함
    • caculatePay: 회계팀에서 기능정의, CFO 보고를 위해 사용
    • reportHours: 인사팀에서 기능정의, COO 보고를 위해 사용
    • save: DBA가 기능을 정의, CTO 보고를 위해 사용
  • 예시
    • caculatePay(CFO)reportHours(COO)가 하나의 메서드를 공유해서 사용함
      • regularHours: 초과 근무를 제외한 업무 시간을 계산하는 알고리즘
    • CFO 팀에서 초과 근무를 제외한 업무 시간 계산 방식을 수정하기로 결정
      • regularHours를 수정하면 COO팀이 사용중인 reportHours에도 영향이 발생
      • COO팀은 변경을 원하지 않음
      • 개발자는 CFO팀이 사용중인 기능들만 테스트
  • 위 예시는 서로 다른 액터가 의존하는 코드가 가까이 배치되어있기 때문에 원하지 않는 결과가 발생
  • SRP는 서로 다른 액터가 의존하는 코드를 분리하라고 말함

징후 2: 병합

  • 소스 파일에 다양하고 많은 메서드를 포함하면 병합이 자주 발생
    • 특히 이들 메서드가 서로 다른 액터를 책임진다면 병합이 발생할 가능성이 높음
  • 예시
    • CTO팀에서 Employee 테이블 스키마 수정
    • COO팀에서 reportHours 메서드의 보고서 포맷 변경
    • 각 팀에서 같은 코드를 수정하면 충돌이 발생, 즉 병합이 발생
      • 충돌이라는 표현이 있지만 같은 영역을 수정해야해서 병합이 필요한 상황을 의미
    • 병합은 항상 위험이 뒤따름
      • 위 상황에서는 CFO팀도 영향을 받을 수 있음
  • 위 예시는 많은 사람이 서로 다른 목적으로 동일한 소스 파일 변경을 시도했기 ㄸ/ㅐ문
  • 이것을 벗어나는 것이 서로 다른 액터를 뒷받침하는 코드를 분리하는것

해결책

  • 데이터와 메서드를 분리하는것
    • 각 팀별로 클래스를 정의, 클래스가 공통 데이터에 접근하도록 구성
    • 세 가지 클래스를 인스턴스화하고 추적해야한다는 단점이 존재
      • 퍼사드(Facade)패턴을 사용해 해결가능
  • EmployeeFacade 클래스
    • 각 팀에서 정의한 세개의 클래스의 객체를 생성
    • 요청된 메서드를 객체로 위임

결론

  • 단일 책임 원칙은 메서드와 클래스 수준의 원칙
  • 상위 개념에서 다른 형태로 등작함
    • 컴포넌트 수준: 공통 폐쇄 원칙(Common Closure Principle)
    • 아키텍처 수준: 아키텍처 경계(Architecture Boundary)의 생성을 책임지는 변경의 축(Axis of Change)