Skip to content

Latest commit

 

History

History
110 lines (77 loc) · 7.48 KB

20240324.md

File metadata and controls

110 lines (77 loc) · 7.48 KB

오브젝트 책 읽기 스터디

5. 책임 할당하기(cont.)

2. 책임 할당을 위한 GRASP 패턴

창조자에게 객체 생성 책임을 할당하라

  • CREATOR(창조자) 패턴: 객체를 생성할 책임을 할당하는 것

  • 아래 조건을 최대한 많이 만족하는 객체가 객체 생성 책임을 할당 받는 것이 좋다.

    • 생성할 객체를 포함하거나 참조한다.
    • 생성할 객체를 기록한다.
    • 생성할 객체를 긴밀하게 사용한다.
    • 생성할 객체를 초기화하는 데 필요한 데이터를 가지고 있다. (정보 전문가)
  • CREATOR 패턴은 어떤 방식으로든 생성되는 객체되는 객체와 연결되거나 관련될 필요가 있는 객체에 해당 객체를 생성할 책임을 맡기는 것이다.

  • 생성하는 객체와 생성될 객체는 서로 결합된다.

    • 이미 결합돼 있는 객체에서 생성 책임을 할당하는 것은 전체적인 결합도에 영향을 미치지 않는다.
  • CREATOR 패턴은 이미 존재하는 객체 사이의 관계를 이용하기에 낮은 결합도를 유지할 수 있다.

3. 구현을 통한 검증

DiscountCondition 개선하기

  • 변경에 취약한 클래스: 코드를 수정해야 하는 이유를 하나 이상 가지는 클래스

    • 낮은 응집도를 가짐
  • 낮은 응집도를 해결하기 위해서는 변경의 이유에 따라 클래스를 분리해야 한다.

  • 변경의 이유가 하나 이상인 클래스에는 몇 가지 패턴이 존재한다.

  1. 인스턴스 변수가 초기화되는 시점을 살펴보기

    • 응집도가 높은 클래스는 인스턴스를 생성할 때 모든 속성을 함께 초기화한다.
    • 응집도가 낮은 클래스는 객체 속성 중 일부만 초기화하고 일부는 초기화되지 않은 상태로 남긴다.
    • 따라서 함께 초기화되는 속성을 기준으로 코드를 분리해야 한다.
  2. 메서드들이 인스턴스 변수를 사용하는 방식을 살펴보기

    • 모든 메서드가 객체의 모든 속성을 사용한다면 클래스의 응집도는 높다.
    • 메서드들이 사용하는 속성에 따라 그룹이 나뉜다면 클래스의 응집도는 낮다.
    • 따라서 속성 그룹과 해당 그룹에 접근하는 메서드 그룹을 기준으로 코드를 분리해야 한다.

다형성을 통해 분리하기

  • 동일한 책임을 가지는 경우 역할의 개념을 사용하자

  • 역할을 구현하기 위해서는 추상 클래스나 인터페이스를 사용하면 된다.

  • POLYMORPHISM(다형성) 패턴

    • 객체의 타입에 따라 변하는 로직이 있을 때 책임을 할당하는 방법
    • 변하는 로직틀 타입으로 정의하고 각 타입에 따라 다형적으로 행동하는 책임을 할당하자.
    • ifelse, switchcase 등으로 조건에 따른 분기를 하지 말고 다형성을 이용해 확장성을 고려하자.
  • 하나의 클래스가 여러 타입의 행동을 구현하고 있는 것처럼 보인다면 클래스를 분리하고 POLYMORPHISM 패턴에 따라 책임을 분산시키자.

변경으로부터 보호하기

  • PROTECTED VARIATIONS(변경 보호) 패턴

    • 변경을 캡슐화하도록 책임을 할당하는 것 -> 책임 할당의 관점에서 캡슐화를 설명한 것
    • 변화가 예상되는 불안정한 지점들을 식별하고 그 주위에 안정된 인터페이스를 형성하도록 책임을 할당하자.
    • 변경될 가능성이 높으면 캡슐화하라.
  • 예측 가능한 변경으로 인해 여러 클래스들이 불안정해진다면 PROTECTED VARIATIONS 패턴에 따라 안정적인 인터페이스 뒤로 변경을 캡슐화하라.

변경과 유연성

  • 개발자로서 변경에 대비할 수 있는 두 가지 방법

    • 코드를 이해하고 수정하기 쉽도록 최대한 단순하게 설계하는 것
    • 코드를 수정하지 않고도 변경을 수용할 수 있도록 코드를 더 유연하게 만드는 것
  • 대부분은 전자가 더 좋지만, 유사한 변경이 반복적으로 발생한다면 두 번째 방법이 더 좋다.

  • 복잡성을 줄이기 위해 상속 대신 합성을 사용하자

  • 객체지향이 어려운 이유가 바로 책임 할당의 어려움 때문이다.

    • 어려울 경우 절차형 코드로 실행되는 프로그램을 빠르게 작성한 뒤, 완성된 코드를 객체지향적인 코드로 변경해보자.

4. 책임 주도 설계의 대안

  • 책임 주도 설계에 익숙해지기 위해서는 많은 노력과 시간이 필요하다.

    • 어느 정도 경험을 쌓은 숙련자도 적절한 책임과 객체를 선택하는 일에 어려움을 느끼곤 한다.
  • 책임과 객체 사이에서 방황할 때 빠른 방법은 일단 기능을 수행하는 코드를 작성하는 것이다.

    • 일단 실행되는 코드를 얻고 난 후, 코드 상에서 명확하게 드러나는 책임들을 이동시키기
  • 코드를 수정한 후 겉으로 드러나는 동작이 바뀌어서는 안된다.

    • 캡슐화, 응집도, 결합도는 변경되어도 동작은 그래도 유지해야 한다.
  • 이해하기 쉽고 수정하기 쉬운 소프트웨어로 개선하기 위해 겉으로 보이는 동작은 바꾸지 않은 채 내부 구조를 변경하는 것을 리팩터링(Refactoring) 이라고 부른다.

메서드 응집도

  • 긴 메서드는 다양한 측면에서 코드의 유지 보수에 부정적인 영향을 미친다.

    • 어떤 일을 수행하는지 한눈에 파악하기 여렵기에 코드를 전체적으로 이해하는 데 너무 많은 시간이 걸린다.
    • 하나의 메서드 안에서 너무 많은 작업을 처리하기 때문에 변경이 필요할 때 수정해야 할 부분을 찾기 어렵다.
    • 메서드 내부의 일부 로직만 수정하더라도 메서드의 나머지 부분에서 버그가 발생할 확률이 높다.
    • 로직의 일부만 재사용하는 것이 불가능하다.
    • 코드를 재사용하는 유일한 방법은 원하는 코드를 복사해서 붙여넣는 것뿐이므로 코드 중복을 초래하기 쉽다.
  • 이런 메서드를 몬스터 메서드(monster method) 라고 부른다.

  • 응집도가 낮은 메서드는 로직의 흐름을 이해하기 위해 주석이 필요한 경우가 대부분이다.

    • 주석을 추가하는 대신 메서드를 작게 분해해서 각 메서드의 응집도를 높이자.
  • 클래스의 응집도와 마찬가지로 메서드의 응집도를 높이는 이유도 변경과 관련되어 있다.

  • 작은 메서드로 조합된 메서드는 마치 주석들을 나열한 것처럼 보이기 때문에 코드를 이해하기도 쉽다.

    • 작은 메서드는 이름을 잘 지었을 때만 그 진가가 들어난다.
  • 너무 많은 세부사항을 기억하도록 강요하는 코드는 이해하기도 어렵다.

  • 메서드를 응집도 높게 분리했으면, 이 메서드들을 적절한 데이터를 정의하고 있는 클래스로 분리해야 한다.

객체를 자율적으로 만들자.

  • 책임 주도 설계 방법에 익숙하지 않다면 일단 데이터 중심으로 구현한 후 이를 리팩터링해도 유사한 결과를 얻을 수 있다.
  • 처음부터 책임 주도 설계 방법을 따르는 것보다 동작하는 코드를 작성한 후에 리팩터링하는 것이 더 훌륭한 결과물을 낳을 수도 있다.
  • 캡슐화, 결합도, 응집도를 이해하고 객체지향 원칙을 적용하기 위해 노력한다면 책임 주도 설계 방법을 단계적으로 따르지 않고도 유연하고 깔끔한 코드를 얻을 수 있을 것이다.