public class Employee {
public Money calculatePay();
public void save();
public String reportHours();
}
2017-08-04
엉클 밥 2014–05–08
1972년에 데이비드 파나스가 내놓은 고전적인 논문 “Criteria To Be used in decomposing systems into modules”. 이 논문은 Communications of the ACM Vol 15 No 12에 게제되었습니다.
이 논문에서 파나스는 간단한 알고리즘을 분해(Decompose)하고 분리(Seprate)하는 두가지 전략을 비교하고 있습니다. 이 논문은 매우 흥미롭게 읽을수 있기 때문에 읽어 보는 것을 강력 추천합니다. 파나스가 내린 결론은 다음과 같습니다.
“우리는 플로우 차트를 기반으로 시스템을 분해 하려는 시도가 거의 항상 실패한다는 예를 보였습니다. 우리는 대신에 디자인 결정 사항(어렵거나, 변경하고자 하는)에 기반하여 분해를 시작하는 것을 추천합니다. 각 모듈은 그런 결정을 다른 사람에게 숨기도록 설계 되어야 합니다.”
파나스가 기술한 내용을 다시 강조합니다. 파나스의 결론은 모듈은 모듈이 변경될수 있는 방식에 따라 분리되어야 한다는 것입니다.
2년후, Edsger Dijkstra는 또 다른 고전 논문을 썼습니다. 이 논문에서 그는 “관심사항의 분리(Separation of Concerns)”라는 용어를 소개했습니다.
1970~80 년대는 소프트웨어 아키텍처가 풍요롭게 된 시기입니다. 구조적 프로그래밍과 설계가 널리 퍼졌고, 그 시기에 결합(Coupling)과 응집(Cohension)이라는 개념이 Larray Constantine에 의해 소개되고 Tom DeMarco등에 의해 확산되었습니다.
1990년대 말에 저는 이런 용어들을 원칙으로 통합하려는 시도를 했습니다. 단일 책임 원칙(Single Responsibility Priciple)이라고 하죠.
단일 책임 원칙(SRP)는 각 소프트웨어 모듈마다 변경해야 하는 이유가 하나만 있어야 한다고 기술하고 있습니다. 멋지죠? 그리고 이것은 파나스의 공식과 일치하는것처럼도 보입니다. 하지만 이 기술은 또다른 질문을 가져옵니다:
“변경해야 하는 이유”의 정의는 뭐지?
어떤사람은 버그 수정이 변경 이유인가?를 궁굼해하고 또 어떤 사람은 리팩토링이 변경이유인가를 궁굼해합니다. 이런 질문들은 “변경해야 하는 이유”와 “책임”이 결합된다고 지적함으로 답변될수 있습니다.
명백하게 코드에게는 버그 수정이나 리팩토링에 대한 책임이 있을수 없습니다. 그것은 프로그래머의 책임이지 프로그램의 책임이 아닙니다.
그럼 프로그램은 무엇에 대해 책임을 지나요? 더 나은 질문은: 프로그램을 책임지는 사람은 누구인가요? 입니다. 더 나은 질문으로 나아가자면: 누가 프로그램의 설계에 대응합니까?
일반적인 비지니스 조직을 상상해보죠. CEO가 꼭대기에 있죠. CFO,COO, CTO등의 C-레벨의 경영자들은 CEO에게 보고합니다. CFO는 재정을 관리하고, COO는 운영을 관리하고 CTO는 기술 인프라 및 개발을 관리합니다.
자 이제 다음 자바 코드를 보세요
public class Employee {
public Money calculatePay();
public void save();
public String reportHours();
}
calculatePay 메소드는 해당 직원의 계약, 상태, 근무 시간 등을 기준으로 특정 직원의 지급액을 결정하는 알고리즘을 구현합니다.
save 메소드는 Employee오브젝트가 관리하는 데이터를 엔터프라이즈 데이터베이스에 저장합니다.
reportHours 메소드는 감사원이 직원이 적절한 시간 동안 작업하고 적절한 보상을 받고 있는지 확인하는 데 사용하는 보고서에 추가되는 문자열을 반환합니다.
자 CEO에게 보고하는 C-레벨 경영진들중에서 누가 calculatePay 메소드의 행위를 정의할 책임을 가질까요? 누가 비극적으로 잘못 정의된 calculatePay 로 인해 CEO에게 해고당할까요?
당연히 대답은 CFO입니다. 직원의 임금을 정의하는 것은 재정적 책임(Responsibility)입니다. 만약 CFO 조직에서 잘못 정의된 임금 계산 규칙으로 직원들이 1년동안 월급을 두배씩 받아 갔다면, CFO가 해고 될 겁니다.
다른 C-Level 담당자는 reportHours메서드 에서 반환 된 문자열의 형식과 내용을 지정해야합니다 . 그 임원은 감사 및 검토자를 관리하며, 이는 운영 책임입니다. 따라서 해당 보고서에서 숫자가 좀 크게 다르게 나왔다면 COO가 해고 될 것입니다.
마지막으로 save 메소드가 비극적으로 잘못 정의 되었다면 어떤 C-레벨 경영자가 해고 될지는 명백하죠. 만약 기업 데이터베이스가 잘못 정의된 내용으로 인해 손상(corrupted)되었다면 CTO가 해고 될겁니다.
이렇게 calcualtePay의 알고리즘을 변경해야 하되는 이유는 CFO 가 이끄는 조직에서 생성된 요청으로 발생합니다. 마찬가지로 reportHours 메소드를 변경해야 하는 이유는 COO 조직에서, save 메소드를 변경해야 하는 이유는 CTO 조직에서 요청하게 됩니다.
이것이 단일 책임 원칙의 핵심입니다. 이 원칙은 사람에 관한 것입니다.
우리가 소프트웨어 모듈을 작성할때, 우리는 변경 요청이 한명 또는 아주 좁게 정의된 비지니스 기능를 대표하는 그룹에서 나오기를 원합니다. 우리는 우리가 작성하는 모듈이 조직 전체의 복잡성에서 분리되기를 원합니다. 그리고 우리는 각 모듈이 딱 하나의 비지니스 기능을 책임지게(대응하게) 시스템을 설계합니다. 왜 일까요? 왜냐하면 CTO의 요청으로 수정한 내용 때문에 COO가 해고되게 하고 싶지 않기 때문입니다.
고객과 관리자를 두렵게 하는것은 자신이 변경을 요청했는데 변경과 전혀 상관 없어 보이는 곳에서 오작동하는 것입니다.
만약 당신이 calculatePay 메소드를 변경했는데 무심코 reportHours 메소드를 고장냈습니다. 그럼 COO는 calculatePay 메소드를 절대로 수정하지 말라고 요청하기 시작 할겁니다.
창문이 내려가지 않아서 정비사에게 차를 가져갔다고 생각해보세요. 다음날 정비사가 다 고쳤다고 합니다. 그래서 차를 타 보았는데 창은 잘 내려갑니다. 그런데 시동이 안걸려요. 아마 당신은 그 정비사를 멍청이라고 생각하고 두번 다시 차를 가져가지 않을겁니다.
이게 변경해달라고 요청하지 않는 부분이 고장 났을 때 바로 고객과 관리자가 느끼는 감정입니다.
이것이 바로 JSP안에 SQL을 두지 않는 이유입니다. 결과를 계산 하는 모듈에서 HTML을 생성하지 않는 이유이기도 하죠. 또한 비지니스 규칙이 데이터베이스 스키마를 몰라야 하는 이유입니다.
이것이 우리가 관심사항을 분리하는 이유입니다.
단일 책임 원칙에 대한 또다른 표현은 다음과 같습니다:
“같은 이유로 변하는 것은 하나로 모으세요. 다른 이유로 변하는 것은 분리하세요.”
잘 생각해보시면 이 문장은 응집(Cohension)과 결합(Coupling)에 대해 정의하는 또다른 방법이라는 것을 깨닫게 될겁니다. 우리는 같은 이유로 변해야 하는 것들에 대해 응집성을 높이고, 다른 이유로 변경하는 것들에 대한 결합성을 낮추기를 원합니다.
하지만 이 원칙을 고려할때, 변경해야 하는 이유는 바로 사람이라는 것을 기억하세요. 사람이 변경을 요청합니다. 그리고 다른 사람들이 다른 이유로 관심있어 하는 내용을 코드로 뒤섞어서 사람들을(자신을) 혼동하지 말아야 합니다.
OOP
OOD
SRP
design pattern