이 분은 Object Mentor 의 리더이자,  Clean Code의 저자인 Bob 삼촌 (Uncle Bob – Robert C. Martin) 의 글이 “모든 프로그래머가 알아야할 97가지” 에 실려 있습니다.

저도 사실 뭔가 재미난 이야기를 해 줄거라고 했는데… 저희에게 너무나도 익숙한 객체지향의 중요한 원칙인   SOLID 의 S인 SRP를 이야기를 하셨네요.

익숙하지만 정말 중요한 원칙인 SRP 이야기를 다시 한번 들어보시길 바랍니다.

Single Responsibility Principle written by Uncle Bob

좋은 설계를 위한 가장 기본적인 원칙 중 하나는 다음과 같습니다.

동일한 이유로 변경되는 것들은 함께 모으고, 서로 다른 이유로 변경되는 것들은 분리시킨다.

이 원칙은 종종 단일 책임의 원칙(Single Responsibility Principle, SRP)이라고도 알려져 있습니다.   한마디로 말해서, 하나의 서브시스템, 모듈, 클래스, 또는 심지어 함수에 대해서도 한 가지 이상의 변경 이유가 있어서는 안 된다는 것을 의미합니다.

이를 설명하기 위한 전형적인 예제로, 비즈니스 룰, 결과 보고서 작성, 데이터베이스 연산을 다루는 메소드들을 함께 가진 클래스를 들 수 있습니다.


public class Employee
{
public Money calculatePay () …
public String reportHours () …
public void save() …
}

어떤 프로그래머들은 한 클래스 안에 이 세 가지 기능이 함께 정의된 것이 가장 적절하다고 생각할지도 모릅니다. 결국 클래스라는 것은 공통의 변수들을 이용하여 연산을 수행하는 함수들의 집합체라고 생각하기 때문입니다. 그러나 문제는, 이 세 가지 함수는 전혀 다른 이유로 변경될 수 있다는 것입니다.
  • calculatePay 함수는 급료를 계산하는 비즈니스 룰이 바뀔 때 매번 변경되어야 합니다.
  • reportHours 함수는 누군가가 기존과 다른 보고서 포맷을 원할 때, 그리고
  • save 함수는 데이터베이스 관리자가 데이터베이스 스키마를 바꿀 때마다 변경되어야 합니다.

이렇듯 세 가지 변경 원인이 있다는 사실은 Employee 클래스를 불안정하게 만드는데, 이는 세 가지 원인 중 한 가지만 발생하여도 클래스가 바뀌어야 하기 때문입니다. 더 중요한 것은, Employee 에 의존하고 있는 클래스들 역시 이 변경으로 영향을 받을 수 있다는 것입니다.

좋은 시스템 설계는 시스템을 독립적으로 배포될 수 있는 컴포넌트들로 분할하는 것입니다. 독립적 배포란 우리가 특정 컴포넌트를 변경하더라도 그 외 다른 컴포넌트들은 재 배포될 필요가 없다는 것을 의미합니다. 그러나 위 예제의 경우, 타 컴포넌트들에 있는 다수의 클래스에서 Employee 를 사용하고 있다면, Employee 가 변경될 때마다 그 컴포넌트들 또한 재 배포 되어야 합니다. 결국 이는 컴포넌트 기반 설계(혹은 서비스 지향 아키텍처 설계)의 주요 이점을 잃게 되는 결과를 초래하게 됩니다. 다음 코드는 간단하게 클래스 분할을 통해 이 이슈를 해결하였습니다.


public class Employee
{
public Money calculatePay() ...
}

public class EmployeeReporter
{
public String reportHours(Employee e) ...
}

public class EmployeeRepository
{
public void save(Employee e) ...
}

분리된 클래스들은 각각 자신의 컴포넌트에 위치할 수 있게 됩니다. 좀 더 정확히 말하면, 결과 보고를 담당하는 모든 클래스는 ‘보고’ 컴포넌트로, 데이터베이스와 관련된 모든 클래스는 ‘저장소’ 컴포넌트로, 그리고 모든 비즈니스 룰에 대한 클래스는 ‘비즈니스 룰’ 컴포넌트로 들어갈 수 있게 됩니다. 재빠른 독자라면 위의 해결책에도 여전히 의존관계가 존재한다는 것을 알 수 있을 겁니다.

Employee 는 여전히 다른 클래스들에 의해 참조되고 있기 때문에, Employee 가 수정되면 다른 클래스들도 재 컴파일되고 재 배포 되어야 할 것입니다. 따라서 우리는 Employee 를 수정하여 독립적으로 재 배포 할 수는 없습니다. 그러나 그 외 다른 클래스들은 수정 및 독립적인 재 배포가 가능해집니다. 그들 중 어떤 클래스가 변경되더라도, 그 외 다른 클래스들을 재 컴파일 하고 재 배포할 필요가 없게 되는 것이지요. 만약 우리가 의존관계 역전의 법칙(Dependency Inversion Principle, DIP)을 잘 사용한다면, Employee 역시 독립적으로 재 배포가 가능해 질 것입니다. 의존 관계 역전의 법칙은 다른 책*의 주제이므로 여기에서 다루지는 않겠습니다.

서로 다른 이유로 변경되는 것들을 잘 분리해야 한다는 단일 책임의 원칙을 잘 적용하는 것이 독립적으로 배포가 가능한 컴포넌트 구성을 만들 수 있는 중요한 비결 중 하나라는 것을 기억하시기 바랍니다.

*Agile Software Development, Principle, Patterns and Practices.  http://www.amazon.com/dp/0135974445/

Join the conversation! 5 Comments

  1. SRT에서 R의 정확한 정의가 필요합니다. 전 작은 기능단위의 R은 좋지 않다고 생각합니다. 너무 많은 Fragmentation과 Relation을 만들기 때문이죠. 실제로 상용 S/W 중에 SRT를 매우 잘지키는 것들이 있는데, 객체지향적으로는 훌륭하나 Readability가 매우 떨어집니다. Concrete를 바로바로 알기가 어렵죠.
    영수님 오랜만이에요..^^

    응답
    • 사실 사용성과 확장성(유연성)과는 자주 상충됩니다.
      이 두 가지를 만족하기란 그리 쉽지가 않죠. ^^
      이건 아마도 저희들의 영원한 숙제가 될지도 🙂

      97 아키텍트에 도움이 될만한 글이 있습니다.

      일반화 이전에 단순화, 재사용성 이전에 사용성
      Simplicity Before Generality, Use Before Reuse

      설계의 기준으로 불확실성을 사용해라.
      https://arload.wordpress.com/2009/09/24/use-uncertainty-as-a-driver/

      응답
  2. 설계에 대해서는 잘 모르지만, 말씀하신 “상용S/W”라서, release가 회사 내부규칙에 따라 정해 지는 S/W라면 모르겠지만, 저와 같이 고객이 “변경해내!” 라고 말하면 그때 그때 배포하는 S/W라면, 조금 readability가 떨어지더라도, SRT를 적용하려 애썼습니다…. 만..
    결국, 해당 기능이 잘 고쳐지지 않고, fixed된 상태를 꽤 오래 유지하면, 다시, 말씀하신대로, 하나의 class로 refactoring 되는 (?) 경우도 있었습니다.
    왜냐하면, 위의 예제에서 처럼

    save() 함수 같은 경우 employee객체 e 에 대한 참조가 save() 안에서 빈번하게 이루어지기 때문이죠.

    file->Write(e->Name());
    file->Write(e->Age());
    ..

    이런식으로요..

    응답
  3. 외부로 공개되지않는 api 나 frmamework와 같이 api 사용성 부분이 중요하지 않으면, 당연히 확장성에 초점을 맞추어야 겠지요. 좋은 접근 입니다. 🙂

    결국 서로 계속해서 호출을 주고 받아서 엮인다면 이 녀석들끼리 ParameterObject 또는 Domain Context Object 형태로 묶어서 퉁 처리하시는게 좋을듯 합니다.

    응답
  4. 저도 동의합니다. 단, 계속 해당 업무에 F/U을 할꺼라면 말이죠.
    제 3자가 유지보수할꺼라는 가정아래 약간의 타협이 필요하다고 생각합니다.
    물론 제 3자가 복잡한 구조라도 많은 스태미나를 사용하여 구조를 정확히 파악하여 유지보수 한다면 타협이 필요없을겁니다. 그런 제 3자는 찾아보기 어렵죠. 아마 바로 메일이나 전화를 할겁니다.

    설계, 리팩토링등은 메인 개발자가 있는 상황에서는 구조적으로 좋은 방향으로 가는 것이 좋겠지만, 그 뒷일까지 생각하는게 나중에 전화를 덜 받을수 있는 현명한 길일수도 있다고 생각합니다. 그리고 확장성이 좋다는 것은 확장을 하고 나서야 알수 있지 않을까요^^;; 잘모르는데 너무 주저리 떠들었네요;; 죄송;;

    응답

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중

This site uses Akismet to reduce spam. Learn how your comment data is processed.

카테고리

Articles, Books & Articles, News, Pattern, Software Architecture

태그

, , , , ,