단일 책임 원칙은 함수는 단 하나의 일만 해야 한다는 원칙이 아니라 사실은 "단일 모듈은 변경의 이유가 하나, 오직 하나뿐이어야 한다." 라는 의미이다.
우발적 중복
단일 책임 원칙을 위반하는 대표적 사례인 우발적 중복을 살펴보자.
Employee
클래스가 아래 세 가지 메서드를 가지고 있다.
calculatePay()
: 회계팀에서 기능을 정의하며, CFO 보고를 위해 사용reportHours()
: 인사팀에서 기능을 정의하며, COO 보고를 위해 사용save()
: 데이터베이스 관리자가 기능을 정의하며, CTO 보고를 위해 사용
calulatePay()
메서드와 reportHours()
메서드가 업무 시간을 계산하는 regularHours()
메서드를 동시에 사용한다고 했을때 CFO 팀에서 업무 시간을 계산하는 방식을 수정하기로 결정했다고 가정하자. 개발자는 calculatePay()
메서드에서 regularHours()
메서드를 사용하는 것을 발견했고, regularHours()
메서드를 수정했다. 이 일을 COO 팀에서는 전혀 인지하지 못했고 결국 큰 예산을 지출하는 실수가 발생했다.
이 문제는 서로 다른 액터(CFO와 COO)가 의존하는 코드를 너무 가까이 배치했기 때문에 발생한다.
SRP는 서로 다른 액터가 의존하는 코드를 서로 분리하라고 말한다.
병합
이번에는 DBA가 속한 CTO 팀에서 Employee 테이블 스키마를 수정하기로 결정했다고 가정하자. 동시에 COO 팀은 reportHours()
메서드의 보고서 포맷을 변경하기로 결정했다고 할 때 Employee
클래스 소스파일을 두 팀에서 동시에 수정하고 결국은 충돌이 일어날 것이다.
병합-충돌 문제를 해결하는 방법은 서로 다른 액터를 뒷받침하는 코드를 서로 분리하는 것이다.
해결책
가장 간단하게는 Employee
클래스는 데이터만 담고, 이를 사용하는 PayCalculator
, HourReporter
, EmployeeSaver
클래스를 새로 만드는 것이다.
또는 PayCalculator
, HourReporter
, EmployeeSaver
모두를 사용하는 EmployeeFacade
를 두는 것도 방법이고, Employee
클래스에 모든 메서드를 구현한 퍼사드로 두고, 이를 구현한 HourReporter
, EmployeeSaver
를 두는 것도 방법이다.