💡 단일 책임 원칙
단일 책임 원칙이란
- 단일 모듈은 변경의 이유가 하나, 오직 하나 뿐이어야 한다는 원칙
- 함수는 하나의 일만 해야 한다는 것은 커다란 함수를 저수준의 함수로 쪼개는 과정의 리팩터링에서 사용되는 말이며 이는 SRP와 일맥상통 하는 말이 아니며, SOLID 원칙도 아님
- 하나의 모듈은, 오직 하나의 액터에 대해서만 책임 져야 한다는 원칙
- 액터 → 해당 변경을 요청하는 한명 이상의 집단
- 모듈 → 소스파일 / 함수와 데이터 구조로 구성된 응집된 집합
- 응집성 → 단일 액터를 책임지는 코드를 함께 묶어주는 힘
- 즉, 하나의 액터가 요청한 것에 대한 코드 변경은 하나의 모듈에서 이루어 지는 것이 이상적이라는 것
단일 책임 원칙을 지키지 않은 예시
class Employee() {
fun calculatePay() // CFO가 필요한 함수 ( 회계팀에서 기능을 정의 )
fun reportHours() // COO가 필요한 함수 ( 인사팀에서 기능을 정의 )
fun save() // CTO가 필요한 함수 ( DBA가 기능을 정의 )
}
- 위 세가지 메서드가 있는 Employee 클래스는, 세명의 액터 ( CTO / CFO / COO )를 책임 지고 있기 때문에 SRP에 위배 됨
- Employee 클래스 안에 세 액터가 결합되어 있는 형태 → 각 액터에 필요에 의한 변경이 다른 액터에 영향을 줄 수 있음
- SRP에서는, 서로 다른 액터가 의존하는 코드를 분리 하는것이 원칙
- 또, 위 코드의 경우 서로 다른 액터가 개발 후 형상 관리 프로그램에서 merging시 conflict가 발생 할 수 있음
해결책
클래스 분리
- 메서드를 각기 다른 클래스로 이동시키는 방식
- 아무런 메서드가 없는 Data구조를 만들고, 각 클래스를 만든 뒤 세개의 클래스가 공유하도록 함
data class EmployeeData()
class PayCalculator() {
fun calculatePay()
}
class HourReporter() {
fun reportHours()
}
class EmployeeSaver() {
fun saveEmployee()
}
- 각 클래스를 분리 함으로써, 세 클래스는 서로의 존재를 모를 수 있음 ( 몰라야 함 ) → 코드 의존성이 없어짐
- 하지만, 위 경우는 개발자가 일일히 클래스를 생성하거나, 관리 해주어야 함
Facade pattern
- 대리인을 두는 Facade pattern을 사용 할 수 있음
class EmployeeFacade() {
val payCalculator: PayCalculator by inject()
val hourReporter: HourReporter by inject()
val employeeSaver: EmployeeSaver by inject()
fun calculatePay() {
payCalculator.calculatePay()
}
fun reportHours() {
hourReporter.reportHours()
}
fun saveEmployee() {
employeeSaver.saveEmployee()
}
}