Developer Log
  • Wiki
  • Books
    • 리눅스 시스템 프로그래밍
      • 핵심 개념 소개
        • 시스템 프로그래밍
        • API와 ABI
        • 리눅스 프로그래밍의 개념
          • 파일과 파일시스템
          • 프로세스
          • 사용자와 그룹
          • 권한
          • 시그널
          • 프로세스간 통신
          • 에러 처리
    • 데이터 베이스 첫걸음
      • 데이터베이스란
        • 데이터베이스의 역할을 생각해 보자
          • 우리와 데이터베이스의 관계
          • 데이터베이스의 기본 기능
          • 데이터베이스 종류
      • 관계형 데이터베이스란
        • 대표적인 DBMS를 알아보자
          • 관계형 데이터베이스란
          • SQL 기초 지식
          • 관계형 데이터베이스를 다루기 위한 사전 지식
      • 데이터베이스에 얽힌 돈 이야기
        • 초기비용과 운영비용을 생각하자
      • 데이터베이스와 아키텍처 구성
        • 다중화에 대해 생각해보자
          • 아키텍처란
          • 데이터베이스의 아키텍처
            • 역사와 개요
              • Stand-alone
              • 클라이언트/서버
              • Web 3계층
            • 가용성과 확장성의 확보
          • DB 서버의 다중화
            • 클러스터링
            • 리플리케이션
          • 성능을 추구하기 위한 다중화 - Shared Nothing
          • 적합한 아키텍처를 설계하기 위해
      • DBMS를 조작할 때 필요한 기본 지식
        • MySQL 설치해보자
        • MySQL과 커넥션 만들기, 데이터베이스에 전화걸기
        • SQL과 관리 명령의 차이
        • 관계형 데이터베이스의 계층
      • SQL 문의 기본
        • SELECT 문으로 테이블 내용을 살펴보자
        • SELECT 문을 응용해보자
        • 데이터를 갱신, 삽입, 제거해보자
        • 뷰를 작성하고 복수 테이블에서 선택해보자
      • 트랜잭션과 동시성 제어
      • 테이블 설계의 기초
      • 백업과 복구
      • 성능을 생각하자
    • 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴
      • 객체 지향
        • 들어가기
        • 객체 지향
        • 다형성과 추상 타입
        • 재사용: 상속보단 조립
      • 설계 원칙 / DI와 서비스 로케이터
        • 설계 원칙: SOLID
          • 단일 책임 원칙(Single Responsibility Principle)
          • 개방 폐쇄 원칙(Open - Closed Principle)
          • 리스코프 치환 원칙(Liskov Substitution Principle)
          • 인터페이스 분리 원칙(Interface Segregation Principle)
          • 의존 역전 원칙(Dependency Inversion Principle)
          • SOLID 정리
        • DI(Dependency Injection)와 서비스 로케이터
      • 주요 디자인 패턴
        • 디지인 패턴이란?
        • 전략(Strategy) 패턴
        • 템플릿 메서드(Template Method) 패턴
        • 상태(State) 패턴
        • 데코레이터(Decorator) 패턴
        • 프록시(Proxy) 패턴
        • 어댑터(Adapter) 패턴
        • 옵저버(Observer) 패턴
        • 미디에이터(Mediator) 패턴
        • 파사드(Facade) 패턴
        • 컴포지트(Composite) 패턴
        • 널(Null) 객체 패턴
        • 팩토리 메서드 패턴
        • 커맨드 패턴
        • 추상 팩토리 패턴
    • 테스트 주도 개발
      • 1부
        • 다중 통화를 지원하는 Money 객체
        • 타락한 객체
        • 모두를 위한 평등
        • 프라이버시
        • 솔직히 말하자면
        • 돌아온 '모두를 위한 평등'
        • 사과와 오렌지
Powered by GitBook
On this page
  • 단일 책임 원칙 위반이 불러오는 문제점
  • 책임이란 변화에 대한 것

Was this helpful?

  1. Books
  2. 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴
  3. 설계 원칙 / DI와 서비스 로케이터
  4. 설계 원칙: SOLID

단일 책임 원칙(Single Responsibility Principle)

객체 지향의 기본은 객체에게 책임을 할당하는 데 있고, 단일 책임 원칙은 아래와 같이 정의한다.

객체는 단 한 개의 책임을 가져야 한다.

객체가 여러 책임을 갖게 되면 해당 객체는 각 책임마다 변경되는 이유가 발생하기 때문에, 객체가 한 개의 이유로만 변경되려면 객체는 한 개의 책임만을 가져야 한다.

단일 책임 원칙 위반이 불러오는 문제점

첫 번째 문제점은 아래와 같다.

한 책임의 구현 변경에 의해 다른 책임과 관련된 코드가 변경될 가능성이 높다는 것이다.

HTTP 프로토콜을 이용해서 데이터를 읽어와 화면에 보여주는 기능이 필요한 상황을 가정해 보자.

public class DataViewer {

  public void display() {
    String data = loadHtml();
    updateGui(data);

  }

  public String loadHtml() {
    HttpClient client = new HttpClient();
    client.connect(url);
    return client.getResponse();

  }

  private void updateGui(String data) {
    GuiData guiModel = parseDataToGuiData(data);
    tableUI.changeData(guiModel);

  }

  private GuiData parseDataToGuiData(String data) {
    ... // 파싱 처리 코드

  }

  ... // 기타 필드 등 다른 코드

}

display() 메서드는 loadHtml()에서 읽어 온 HTML 응답 문자열을 updateGui()에 보낸다. updateGui() 메서드는 parseDataToGuiData() 메서드를 이용해서 HTML 응답 메시지를 GUI에 보여주기 위한 GuiData 객체로 변환한 뒤에 실제 tableUI를 이용해서 데이터를 보여주고 있다.

DataViewer를 잘 사용하고 있는 도중에 데이터를 제공하는 서버가 HTTP 프로토콜에서 소켓 기반의 프로토콜로 변경되었고 응답 데이터는 byte 배열을 제공한다.

이러한 데이터를 읽어 오는 기능의 변화로 인해, 아래와 같이 DataViewer는 내부적으로 변화가 연쇄적으로 발생한다.

위의 그림과 같이, 연쇄적인 코드 수정은 두 개의 책임(데이터 읽는 책임, 화면에 보여주는 책임)이 한 객체에 아주 밀접하게 결합되어 있어서 발생한 증상이다.

책임의 개수가 많아질수록 한 책임의 기능 변화가 다른 책임에 주는 영향은 비례해서 증가하게 되는데, 이는 결국 코드를 절차 지향적으로 만들어 변경을 어렵게 만든다.

이에 대한 해결방안은 아래와 같다.

  1. 데이터 읽기와 데이터를 화면에 보여주는 책임을 두 개의 객체로 분리

  2. 둘 간에 주고받을 데이터를 저수준의 String이 아닌 알맞게 추상화된 타입을 사용

위와 같은 방법을 통해서, 데이터를 읽어 오는 부분의 변경 때문에 화면에 보여주는 부분의 코드가 변경되는 상황을 막을 수 있다. 요약해보면 아래 그림과 같다.

결과적으로, DataLoader 클래스가 내부적으로 구현을 변경하더라도, DataDisplayer는 영향을 받지않는다.

한 객체에 섞여 있던 책임을 두 객체로 분리함으로써 변경의 여파를 줄일 수 있게 되는 것 이다.

단일 책임 원칙을 지키지 않았을 때 발생하는 두 번째 문제는 아래와 같다.

단일 책임 원칙을 지키지 않으면 재사용을 어렵게 한다는 문제가 발생한다.

앞서 DataViewer 예제로 다시 돌아가 보자. HttpClient 패키지와 GuiComp 패키지가 각각 별도의 jar 파일로 제공된다고 가정한다.

이 상태에서 데이터를 읽어 오는 기능이 필요한 DataRequiredClient 클래스를 만들어야 한다면, 구현하기 위해 필요한 것은 DataViewer와 HttpClient jar 파일이다. 하지만, 실제로는 DataViewer가 GuiComp를 필요로 하므로 GuiComp jar 파일까지 필요하다.

즉, 실제로 사용하지 않는 기능이 의존하는 jar 파일까지 필요한 것이다.

단일 책임 원칙에 따라 책임이 분리되었다면, 의 그림과 같이 DataRequiredClient 클래스를 구현할 때에는 데이터를 읽어 오는데 필요한 dataloader 패키지와 HttpClient 패키지만 필요하며, 데이터를 읽어 오는 것과 상관없는 GuiComp 패키지나 datadisplay 패키지는 포함시킬 필요가 없어진다.

책임이란 변화에 대한 것

책임의 단위는 변화되는 부분과 관련되어 있다.

예를 들어, DataViewer 객체에서 데이터를 읽어 오는 기능에 변화가 발생했는데, 이런 변화를 통해 데이터를 읽어 오는 기능이 별도로 분리되어야 할 책임이라는 것을 알 수 있을 것이다.

각각의 책임은 서로 다른 이유로 변경되고, 서로 다른 비율로 변경되는 특징이 있다. 따라서, 서로 다른 이유로 바뀌는 책임들이 한 객체에 함께 포함되어 있다면 해당 객체는 단일 책임 원칙을 어기고 있다고 볼 수 있다.

그러면 어떻게 단일 책임 원칙을 지킬 수 있을까?

방법은 바로 메서드를 실행하는 것이 누구인지 확인해 보는 것이다.

위의 그림과 같이, 객체의 사용자들이 서로 다른 메서드들을 사용한다면 그들 메서드는 각각 다른 책임에 속할 가능성이 높으므로 책임 분리 후보가 된다.

Previous설계 원칙: SOLIDNext개방 폐쇄 원칙(Open - Closed Principle)

Last updated 6 years ago

Was this helpful?