7장 SRP: 단일 책임 원칙
SOLID 원칙 중 의미가 가장 잘 전달되지 못한 원칙이다. 함수는 반드시 하나의 일만 해야 한다
는 원칙은 함수를 리팩토링하는 저수준에서 사용하는 원칙이다. 이것은 SRP가 아니다.
역사적으로 SRP는 다음과 같다.
단일 모듈은 변경의 이유가 하나 뿐이어야 한다.
여기서 '변경의 이유'는 해당 변경을 요청하는 한 명 이상의 사람(액터) 을 가리킨다.
따라서 최종적으로 SRP는 다음과 같다.
하나의 모듈은 하나의 액터에 대해서만 책임져야 한다.
'모듈'은 소스 파일을 말한다. 코드를 소스 파일에 저장하지 않는 경우엔 함수와 데이터 구조로 구성된 응집된 집합을 말한다.
응집(cohesive)이라는 단어가 SRP를 암시한다. 단일 액터를 책임지는 코드를 묶어주는 힘이 응집성(cohesion) 이다.
원칙을 위반하는 징후들을 통해 원칙을 이해해보자.
징후 1: 우발적 중복
급여 애플리케이션의 Employee 클래스는 3가지 메서드 calculatePay()
, reportHours()
, save()
를 가진다. 3가지 메서드는 서로 다른 3명의 액터를 책임진다.
calculatePay()
: 회계팀에서 CFO 보고를 위해 사용reportHours()
: 인사팀에서 COO 보고를 위해 사용save()
: DBA가 CTO 보고를 위해 사용
따라서 Employee 클래스는 SRP를 위반한다.
3가지 메서드가 단일 클래스에 배치해서 결합되었기 때문에 CFO의 요구사항이 COO가 의존하는 무언가에 영향을 줄 수 있다.
이러한 문제는 서로 다른 액터가 의존하는 코드를 가까이 배치했기 때문에 발생한다. SRP는 서로 다른 액터가 의존하는 코드를 서로 분리하라고 말한다.
징후 2: 병합
서로 다른 개발자가 Employee 클래스를 변경하려고 할 때 이들의 변경사항은 서로 충돌한다. 많은 사람이 서로 다른 목적으로 동일한 소스를 변경하는 경우 충돌이 일어난다. 따라서 서로 다른 액터를 뒷받침하는 코드는 분리되어야 한다.
해결책
가장 확실한 해결책은 데이터와 메서드를 분리하는 것이다. 메서드가 없는 EmployeeData
클래스를 만들어 PayCalculator
, HourReporter
, EmployeeSaver
클래스가 공유하도록 만들고 세 클래스가 서로의 존재를 모르게 만들면 '우연한 중복'을 피할 수 있다.
이 방법은 개발자가 3가지 클래스를 인스턴스화하고 추적해야 한다는 단점이 있다. 이때 파사드(facade) 패턴 을 사용한다. EmployeeFacade 클래스를 만들어서 3가지 클래스의 객체를 생성하고 요청된 메서드를 갖는 객체로 위임하는 일을 책임진다.
중요한 비즈니스 규칙을 데이터와 가깝게 유지하려고 하는 경우, Employee
클래스에 중요한 메서드를 그대로 유지하고 Employee
클래스를 덜 중요한 나머지 메서드들에 대한 파사드로 사용할 수 있다.
이 경우 Employee
클래스에서 HourReporter
, EmployeeSaver
클래스의 객체를 생성하고 메서드를 해당 객체로 위임한다.
결론
단일 책임 원칙은 메서드와 클래스 수준의 원칙이다.
Last updated