오브젝트 Object 요약 8~9

Mr_Gu·2022년 1월 17일
0

books

목록 보기
6/8
post-thumbnail

8. 의존성 관리하기

어떤 객체에 의존해야 한다면...

실행 시점 : 의존하는 객체가 제대로 동작하기 위해서 실행 시에 의존하는 객체가 반드시 존재해야 한다.
구현 시점 : 의존 대상 객체가 변경될 경우 의존하는 객체의 클래스 코드도 함께 변경해야 한다.

시점에 따른 의존성 분류

//Movie
public class Movie{
  private DiscountPolicy discountPolicy;
  //...
  
  public Movie(String title, ..., DiscountPolicy discountPolicy){
    this.discountPolicy = discountPolicy;
    //...
  }
  //...
}

//Main
Movie movie = new Movie(new AmountDiscountPolicy(...), ...);

컴파일 타임 의존성 : 코드 단위의 의존성, Movie Class는 DicountPolicy Class에 영향받는다.
런타임 의존성 : 실행 시점에 생기는 의존성, Movie Object는 생성을 위해선 AmountDiscountPolicy 객체가 필요하다.

컴파일 타임때는 추상적인 Class에 의존하고 런타임 때 구체적인 객체에 의존하는 방식으로 유연한 설계를 구상할 수 있다.

컨텍스트 독립성
구체 클래스에 의존하는 것은 클래스의 인스턴스가 어떤 문맥에서 사용할지 구체적으로 명시하는 것과 같다. 코드 작성 시점에서는 최대한 특정 문맥에 종속시켜서는 안된다.

의존성 해결하기

느슨하게 연결된 컴파일 타임 의존성은 구체적으로 실행하기 위해서 런타임 의존성으로 변경해 주어야 한다.

1)객체를 생성하는 시점에 생성자로 의존성 해결
2) 객체 생성 후 setter method를 통해 의존성 해결
3) 메서드 실행 시 인자를 이용해 의존성 해결(일시적으로 의존할 경우)

보통 1)과 2)의 방식을 혼합하여 사용한다. + Factory 패턴을 이용하면 client 측과의 의존성도 낮출 수 있다.

유연한 설계를 위한 조언

  1. 의존성과 결합도, 모든 의존성이 나쁜건 아니지만 과한 의존성은 나쁘다. 재사용이 가능할 정도로만 의존하자.

  2. 지식이 결합을 낳는다.

  • 추상화에 의존하라.

    구체 클래스 => 추상 클래스 => 인터페이스 로 갈수록 해당 객체를 이용하기 위해 알아야 할 지식이 줄어든다.

  • new는 해롭다.

    객체 생성 자체가 해당 객체를 많이 알아야 가능한 일이다. 생성하는 행위 자체가 의존성을 유발한다는 걸 명심하자.
    다만 협력하는 기본 객체를 설정하는 경우처럼 의존성을 포기하는 대신 편의성을 얻는 경우도 많으니 무작정 생성을 반대하지는 말자.

  • 표준 클래스에 대한 의존은 해롭지 않다.

    의존성이 문제가 되는 경우는 의존하는 대상이 변경될 가능성이 있을 경우다. 표준 클래스, 오랜 기간 검증된 라이브러리 등등은 믿고 의존해도 괜찮다.

  1. 명시되지 않은 의존성은 해롭다.

    특정 메서드가 무언가에 의존한다면 꼭 시그니처에 이를 표현해주자. 메서드 구현 부분에만 드러나는 의존성은 나중에 찾기 힘들다.

//Bad
public void show(){
  Movie movie = new Movie()
}
//Modefied
public void show(Movie movie){
	//...
}
  • 컨텍스트 독립적으로 => 조합 가능하도록

9. 유연한 설계

개방 폐쇄 원칙

Open-Closed Principle
소프트웨어 개체는 확장에는 열려있고 수정에는 닫혀 있어야 한다.
핵심은 추상화에 의존하는 것. 무엇을 숨기고 무엇을 드러낼지 열심히 고민해야 좋은 설계를 얻을 수 있다.
구체적으로 다형성을 이용해 컴파일 타임 의존성과 런타임 의존성을 분리하라는 조언이다.

생성 사용 분리

유연한 설계를 원한다면 생성하는 코드와 사용하는 코드는 분리해야 한다.

소프트웨어 시스템은 의존성을 연결하는 시작 단계와 시작 이후 이어지는 실행 단계를 분리해야 한다. [288page]

Factory Object
설계의 편의를 위해 인위적으로 만든 Pure Fabrication 이다. 생성과 관련된 책임을 한쪽으로 몰아주자.

Dependency Injection
생성과 사용을 분리하면 런타임시 사용하는 측에 생성된 객체를 주입시켜 줘야한다. 이를 Dependency Injection이라 부른다. 주입 방법은 위에 의존성 해결하기 부분에서 다루었다.

Service Locator

설계에 의존성 주입만을 담당하는 객체를 생성할 수 있는데 이를 Service Locator 패턴이라고 한다.

Service Locator를 통해 의존성을 관리하기 때문에 객체를 사용하는 측은 사용하는 객체의 생성 정보를 몰라도 된다.

public class Movie{
  private DiscountPolicy discountPolicy;
  
  public Movie(String title, Duration runningTime, Money fee){
    this.title = title;
    this.runningTime = runningTime;
    this.fee = fee;
    // ServiceLocator가 의존성 주입 담당.
    this.discountPolicy = ServiceLocator.discountPolicy();
  }
}

다만, 문제는 Service Locator가 의존성을 숨긴다. Movie의 인터페이스에는 더이상 DiscountPolicy에 의존한다는 정보가 없어지고 의존성을 주입하는 부분도 Service Locator 안에 숨겨진다.

그렇다고 Service Locator 가 무작정 나쁘다는 게 아니다. 정확히 말하면 숨겨진 의존성이 나쁘다는 의미다.

의존성 역전 원칙(DIP)

  1. 상위 수준 모듈은 하위 수준 모듈에 의존해서는 안된다. 둘 다 추상화에 의존하도록 하자.
  2. 추상화는 구체적인 사항에 의존하면 안된다. 구체적인 사항이 추상화에 의존해야 한다.

번외로 패키징을 할 때에도 추상적인 부분과 구체적인 부분을 구분하도록 하자.

위에 구조는 DiscountPolicy가 구체적인 정책과 묶여있기에 구체적인 사항 변경시 Movie에게 까지 영향을 미친다.
그러나 밑에 구조에서는 구체적인 정책이 변화해도 DicountPolicy와는 분리되어 있고 그로인해 Movie에 영향을 끼치지 않는다.

유연성에 대한 조언

유연한 설계는 유연성이 필요할 때만 옳다.
유연한 설계의 숨은 말은 이해하기 어려운 설계라는 점을 명심하자.

profile
그냥... 즐기자..

0개의 댓글