ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [책너두 4기] 개발 서적 스터디 - 오브젝트 chapter 5 ~ 7장 요약
    독서/오브젝트 2023. 7. 16. 17:10

    [13일 ~ 18일차 요약본]

    Chapter.5 책임 할당하기

    • GRASP 패턴을 더 살펴본다.

    구현을 통한 검증

    다형성 적용

    • (GRASP 패턴) POLYMORPHISM 패턴
      • 객체의 타입에 따라 변화하는 행동이 있다면 타입을 분리하고, 변화하는 행동을 각 타입의 책임으로 할당
    • 코드에 DiscountCondition를 인터페이스로 만들고 PeriodCondition, SequenceCondition으로 분리하여 책임 할당

    변경으로 부터 보호

    • (GRASP 패턴) PROTECTED VARIATION 패턴 (변경 보호 패턴)
      • 새로운 할인 조건을 추가할때 기존 PeriodCondition, SequenceCondition은 캡슐화 되어 있고, DiscountCondition을 implement 해서 조건을 추가하는 것 (변경 보호)
      • Movie를 추상클래스로 만들고, 자식들이 구현하게 하는 것
    • PROTECTED VARIATION 패턴, POLYMORPHISM 패턴 두개를 적절히 활용하여 Movie, DiscountCondition을 분리했다.
    • 변경의 이유가 1개인 응집도는 높고, 결합도는 낮은 코드
    • 도메인 구조가 코드의 구조를 이끈다.
      • 변경 역시 도메인 모델의 일부이다.
      • 구현을 가이드할 수 있는 도메인 모델을 선택하라

    변경과 유연성

    • 설계를 주도하는 것은 변경이다.
    • 변경을 대비하는 2가지 방법
      1. 코드를 이해하고 수정하기 쉽도록 단순하게 설계 (가독성)
      2. 코드를 수정하지 않고 변경을 수용하게 만들기 (유연성)
    • 대부분이 1번이 더 좋은 방법이지만, 유사한 변경이 반복적으로 일어난다면 복잡성이 상승하더라도 2번을 선택하는 것이 좋다,
    • 상속 대신 합성을 사용해라 (DiscountPolicy)
    • 유연성은 의존성 관리의 문제이다.

    책임 주도 설계의 대안

    • 여전히 객체지향 설계의 책임 할당이 어렵다면, 최대한 빠르게 목적 기능 수행 코드를 작성하고 리팩터링하 방법이 있다.

    메서드 응집도

    • 긴 메서드는 유지보수에 부정적인 영향을 미친다.
      • 한눈에 파악하기 어렵다
      • 로직 일부 재사용이 불가능
      • 메서드 내부 일부를 수정할때 버그 발생 확률이 높다.
      • 수정 부분을 찾기 어렵다.
    • 이런 메서드 응집도가 낮은 메서드를 몬스터 메서드라고 부른다.
    • 메서드를 작게 분해해서 각 메서드의 응집도를 높여라
      • 이해하기 쉽다.
      • 변경하기도 쉽다.
      • 재사용성도 올라간다.

    객체를 자율적으로 만들자

    • 메서드가 사용하는 데이터를 저장하고 있는 클래스로 메서드를 이동시킨다.
    • 캡슐화, 높은 응집도, 낮은 결합도를 가질 수 있다.
      • ReservationAgency 메서드를 DiscountCondition으로 이동
    • 책임 주도 설계가 익숙하지 않다면, 데이터 중심으로 구현한 후 이를 리팩터링 하여도 유사한 결과를 얻을 수 있다.

    Chapter.6 메시지와 인터페이스

    • 객체지향 프로그래밍은 클래스가 아니라 협력 안에서 객체가 수행하는 책임에 초점을 맞춘다.
    • 책임이 객체가 수신할 메시지의 기반이 된다.
    • 애플리케이션은 클래스로 구성되지만, 메시지로 정의된다.
    • 유연하고 재사용 가능한 메시지 만드는데 도움이 되는 원칙, 기법들을 살펴보자

    01. 협력과 메시지

    • 클라이언트 - 서버 모델 (애플리케션의 그 앞단, 백단의 클라, 서버를 말하는 것이 아님)
      • 협력은 객체가 다른 객체에게 무언가 요청할때 시작된다.
      • 매시지를 전송하는 객체를 클라이언트
      • 메시지를 수신하는 객체를 서버
      • 클라이언트 - 서버는 단방향 상호작용을 한다.
      • 하나의 객체는 클라이언트, 서버 역할을 동시에 가진다.
      • 설계 시 메시지 수신 뿐만 아니라 전송도 함께 고려해야한다.
    • 메시지와 메시지 전송
      • 한 객체가 다른 객체에게 무언가 요청하는 것을 메시지 전송이라 한다.
      • 메시지는 Operation name + argument로 구성된다.
      • messageReceiver.operation(args)
    • 메시지와 메서드
      • 메시지를 수신했을때 실제로 실행되는 함수 또는 프로시저를 메시지라 한다.
        • ex Interface와 구현체, 런타임에 지정된 객체의 함수
    • 퍼블릭 인터페이스와 오퍼레이션
      • 외부에 공개하는 메시지 집합
    • 시그니처
      • 오퍼레이션의 이름과 파라미터 목록읍 합쳐서 시그니처라 부른다.
      • 오퍼레이션은 런타임에 어떤 메서드를 실행할 것인지 정의
    • 메시지가 객체의 품질을 결정한다.

    02.인터페이스와 설계 품질

    • 좋은 인터페이스는 최소한의 인터페이스와 추상적인 인터페이스라는 조건을 충족
      • 인터페이스는 최소 오퍼레이션만 포함한다.
      • 추상 인터페이스는 무엇을 하는지만 표현한다.
    • 책임 주도 설계를 따르면 좋은 인터페이스를 설계할 수 있다.
    • 좋은 퍼블릭 인터페이스를 위한 원칙이 있다.
      • 디미터 법칙
      • 묻지말고 시켜라
      • 의도를 드러내는 인터페이스
      • 명령-쿼리 분리

    디미터 법칙

    • 객체 내부 구조에 강하게 결합되지 않도록 협력 경로를 제한해라
    • 클래스 내부 메서드가 아래 조건을 만족하는 인스턴스에만 메시지를 전송하도록 프로그래밍 해라
      • this 객체
      • 메서드의 매개변수
      • this의 속성
      • this의 속성인 컬렉션의 요소
      • 메서드 내에서 생성된 지역 객체
    • 디미터 법칙을 따르면 부끄럼타는 코드를 짤 수 있다. (shy code)
      • 불필요한 어떤 것도 보여주지 않으며 다른 객체 구현에 의존하지 않는 코드
    • 디미터 법칙은 캡슐화를 다른 관점에서 표현한 것

    묻지 말고 시켜라

    • Tell, Don’t Ask
    • 절차적인 코드는 정보를 얻은 후에 결정하는 반면 객체지향 코드는 객체에게 하도록 시킨다.
    • 객체가 어떤 작업을 수행하는지 노출하지 않는다.

    의도를 드러내는 인터페이스 (Intention Revealing Interface)

    • 켄트 백 저서에 메서드 명명법 2가지 소개
      1. 메서드가 작업을 어떻게 수행하는지 명명
      2. 메서드가 무엇을 하는지 명명
    • 2번 방법을 따르면 설계의 유연성을 향상 시킨다.
    • 어떻게를 드러내는 것은 내부 구현을 드러내는 일이다. 캡슐화 위반
    • 자바에서는 두 메서드 이름 뿐만 아니라 타입 계층까지 묶어줘야한다.
    • 객체의 퍼블릭 인터페이스에는 협력과 관련된 의도 만을 표현해야 한다.

    함께 모으기

    • 디미터 법칙, 묻지말고 시켜라 원칙을 지키면 객체를 자율적인 존재로 만든다.
    • 퍼블릭 인터페이스로 의도를 드러내면 직접 개발한 개발자가 아닌 제 3가자 코드를 봤을때 인터페이스를 해석하기 용이하게 한다.
    • 오퍼레이션 이름은 협력이라는 문맥을 반영해야한다.
    • 법칙을 다같이 적용하면 결합도는 낮으면서 의도를 명확히 드러내는 간결한 협력을 가져갈 수 있다.

    03. 원칙의 함정

    • 설계는 트레이드 오프의 산물이다.
    • 디미터 법칙, 묻지말고 시켜라 원칙에는 예외가 있다.
    • 원칙이 현재 상황에 부적합하다고 판단되면 과감하게 원칙을 무시해라
    • 원칙을 적용할때 고려할만한 이슈를 살펴보자

    디미터 법칙은 하나의 . 를 강제하는 규칙이 아니다.

    • 예를 들어 자바8 스트림에서 다수의 .를 사용하더라도 내부 구조 구현을 숨기기 때문에 디미터 법칙 위반이라 볼수 없다.

    결합도와 응집도 충돌

    • 묻지말고 시켜라, 디미터 법칙 준수가 항상 긍정적인 결과로 귀결되는 것은 아니다.
    • 맹목적으로 따르면 객체가 상관 없는 책임을 떠맡아 응집도가 낮아질 수 있다.
    • 묻는 대상이 객체가 아니라 자료구조라면 내부를 노출시키는게 당연하므로 법칙을 적용할 필요 없다.

    04. 명령-쿼리 분리 원칙

    • 퍼블릭 인터페이스에 오퍼레이션을 정의할때 참고할 수 있는 지침을 제공
    • 객체의 상태를 수정하는 오퍼레이션을 Command라 부른다.
      • Command는 상태를 변경하는 반환값을 가질 수 없다.
      • 부수효과를 발생 (side effect)
    • 객체의 관련된 정보를 반환하는 오퍼레이션을 Query라 부른다.
      • 정보 반환 Query는 상태를 변경할 수 없다.
      • 부수효과가 없다.
    • 명령-쿼리 분리 원칙에 따라 작성된 객체의 인터페이스를 명령-쿼리 인터페이스라 부른다.
    • 분리하면 반환값 존재 여부만 확인해도 간단하게 어떤 메서드가 부수효과를 가지는지 살펴 볼수 있다.
      • 즉 부수효과 제어가 쉬워진다.
      • 디버깅이 빨라진다. 유지보수가 쉬워진다.

    명령-쿼리 분리와 참조 투명성

    • 부수효과 제어가 쉬워진다.
    • 명령이 개입하지 않으면 한 쿼리의 값은 변경되지 않아서 쿼리 결과 예측이 쉬워진다.
    • 참조투명성이란?
      • 어떤 표현식 e가 있을때 e의 값으로 e가 나타나는 모든 위치를 교체하더라도 결과가 달라지지 않는 특성
    • 참조투명성을 만족하면 값이 바뀌지 않는 성질을 불변성이라 부른다.
    • 참조투명정이 주는 장점
      • 식의 계산이 쉬워짐
      • 식의 순서를 변경해도 결과가 같다
    • 객체지향은 상태 변경이라는 부수효과를 기반으로 하기에 참조투명성은 예외에 가깝다.
    • 명령-쿼리 분리 법칙을 사용하면 제한적으로 참조투명성을 가질 수 있다.

    책임에 초점을 맞춰라

    • 위 원칙을 적용하는 쉬운 방법 == 메시지를 먼저 선택하고 그후 메시지를 처리할 객체를 선택하는 것
    • 객체가 수행할 책임을 중심으로 설계하면 아래와 같은 효과
      • 디미터 법칙 : 객체 내부 구조에 강하게 결합되지 않도록 협력 경로를 제한해라 (메시지로 경로 제한)
      • 묻지말고 시켜라 : 클라이언트가 메시지를 통해 협력하기에 메시지 선택만 할뿐, 정보를 요청하지 않는다.
      • 의도가 드러나는 인터페이스 : 메시지 이름이 의도를 드러낸다.
      • 명령-쿼리 분리 원칙 : 협력 속에서 객체의 상태를 예측하고 이해하기 쉽게 바꾸다보면 명령과 쿼리를 분리하게 된다.
    • 이러한 원칙들은 구현과 부수효과를 캡슐화하고, 높은 응집도와 낮은 결합도를 가진 인터페이스를 만드는 지침이 된다.
    • 그러나 원칙들이 실행 시점에 필요한 구체적인 제약이나 조건을 명확하게 표현하지 못한다.
      • 이런 문제를 해결하기 위해 계약에 의한 설계 개념 등장 (부록 A 참고)

    Chapter.7 객체 분해 

    • 객체지향의 탄생 배경, 역사를 살펴본다.

    00. 인간의 기억과 추상화, 분해

    • 인간은 문제를 해결할때 단기기억을 사용한다. 단기기억에는 한계가 있다.
    • 단기기억은 한번에 5~9개 정보만 저장할 수 있다.
    • 단기기억 능력으로 초과하는 일을 해결하려고 하면 인지 과부하로 문제해결 능력이 급격하게 떨어진다.
    • 인지 과부하를 방지하는 방법은 다뤄야하는 정보를 본질만 남기고 세부사항을 걸려내고 생각하면 된다.
    • 이를 추상화라 한다.
    • 가장 일반적인 추상화는 큰 문제를 해결 가능한 작은 문제로 나누는 작업이다. 이를 분해라 한다.
    • 인간의 단기기억과 추상화의 수는 한계가 있지만, 추상화를 더 큰 규모로 추상화하면 기억의 한계를 초월할 수 있다.

    01. 프로시저 추상화와 데이터 추상화

    • 프로그래밍 언어 복잡성을 추상화로 해결하려는 노력은 프로그래밍 패러다임의 탄생으로 이어졌다.
    • 현대적인 프로그래밍 언어를 특정 짓는 두가지 추상화 메커니즘
      • 프로시저 추상화
        • 기능을 중심으로 시스템을 분해
        • 알고리즘 분해
      • 데이터 추상화
        • 데이터를 중심으로 시스템을 분해
        • 타입을 추상화 (추상 데이터 타입)
        • 프로시저를 추상화 (객체지향)
    • 기능을 협력하는 공동체를 구성하도록 객체를 나누는 과정이 객체지향 패러다임에서의 분해를 의미한다.
    • 객체지향은 데이터 추상화와 프로시저 추상화를 함께 포함한 클래스를 이용해 시스템을 분해하는 것이다.

    02. 프로시저 추상화와 기능 분해

    • 객체지향이 전통적인 기능 분해 방법에 비해 효과적인 이유를 살펴본다.
    • 우선 전통적인 기능 분해 방법인 프로시저 추상화를 살펴본다.

    메인 함수로서의 시스템

    • 기능 분해 관점에서 추상화의 단위는 프로시저이다. 시스템은 프로시저 단위로 분해된다.
    • 시스템은 더 작은 작업으로 분해될 수 있는 하나의 main 함수다.
    • 전통적인 기능 분해 방식은 하향식 접근법을 따른다.
      • 하향식 접근법? 최상위 기능을 정의하고 하위 기능으로 분해해 가는 접근법

    급여 관리 시스템

    • 하향식 접근법으로 급여 관리 시스템을 구현하는 예제
    • 최상위 기능을 정의 후 구현이 가능한 수준의 저수준 문장까지 기능 분해 해보기
    • 하향식 접근법은 먼저 필요한 기능을 생각하고 그 기능을 분해, 정제하는 과정에서 데이터의 종류, 저장 방식을 결정한다.
    • [ 예제 ]
    • 최상위 기능 - 직원의 급여를 계산한다.
      • 하위 기능 1 : 사용자에게 소득세율 입력 받는다.
        • 저수준 하위 기능 : 세율 입력하세요 화면 출력
        • 저수준 하위 기능 : 키보드로 세율 입력 받는다.
      • 하위 기능 2 : 직원의 급여를 계산
        • 저수준 하위 기능 : 전역변수 저장된 직원의 기본급 정보 획득
        • 저수준 하위 기능 : 급여 계산
      • 하위 기능 3 : 양식에 맞게 결과를 출력
        • 저수준 하위 기능 : {이름}, {급여} 형식의 출력 문자열 생성

    하향식 기능 분해의 문제점

    • 설계는 변경을 대비하기 위한 것이다.
    • 하향식 접근법은 변경에 취약하다. 만들때는 좋았지만 새로운 요구사항으로 변경이 발행하면 코드 복잡도, 파급효과, 관리 포인트 상승
    • 문제점
      • 하나의 최상위 메인함수 개념은 비현실적이다.
        • 현대 시스템은 동등한 수준의 다양한 기능으로 구성
      • 메인 함수의 빈번한 재설계
        • 새로운 기능 추가할때마다 메인 함수 변경 필요 (조건문, 함수 추가 , 데이터 추가 등등..)
      • 비즈니스 로직과 사용자 인터페이스의 결합
        • 비즈니스 로직과, 인터페이스는 변경 빈도가 다르다.
        • 인터페이스의 빈번한 변경이 비즈니스 로직에 영향을 준다.
      • 성급하게 결정된 실행 순서
        • 하향식 접근법으로 정의한 함수들은 특정 문맥을 강요 받는다. 즉 결합도가 높다. 재사용이 불가하고 변경에 취약하다.
      • 데이터 변경으로 인한 파급효과
        • 어떤 데이터가 어떤 함수를 사용하고 있는지 추적 하기가 어렵다.
        • 데이터 변경이 어떤 영향을 주게 될 것인지 예상하기 어렵다.
    • 데이터 변경의 영향도를 최소화 하려면 데이터와 함께 변경되는 부분을 구현 단위로 묶고 외부 제공되는 함수로만 데이터에 접근해야한다.
    • 이것이 의존성 관리의 핵심이다.
    • 기능 분해의 문제점을 해결하기 위해 정보은닉, 모듈이라는 개념이 제시된다. (by 데이비드 파나스)

    언제 하향식 접근법이 유용한가?

    • 완전히 이해된 사실을 서술하기 적합하다.
    • 문서화가 용이하다
    • 작은 프로그래밍, 개별 알고리즘을 위해서는 유용한 패러다임으로 남아 있다.

    yes24

    반응형
Designed by Tistory.