Objects 8장 의존성(변경) 관리하기

카일·2020년 3월 1일
0

Objects 오브젝트

목록 보기
8/11
post-thumbnail

객체의 세계에서 협력은 필수적이다. 협력이 존재한다는 것은 객체간의 의존성이 발생한다는 의미이며 (메세지를 보내고 받을) 이러한 의존성은 최소화되어야한다. 즉 무분별한 의존성은 오히려 악영향을 주는 것이다.

충분히 협력적이면서도 유연한 객체를 만들기 위해 의존성을 관리하는 방법

의존성이란?

내가 가지고 있는 어떠한 인스턴스나 파라미터를 통해 들어온 값이 변경될 때 나도 같이 변해야한다는 말은 곧 그 객체에 의존하고 있다는 것을 의미한다.

의존성 전이

하나의 객체가 다른 객체에 의존하고 있는 경우 의존하고 있는 객체가 변경되는 경우도 변경이 발생할 수 있다. 이런 경우를 의존성이 전이된다 라고 한다.

의존성 전이를 최소화 하는 방법? : 공부해야함

런타임과 컴파일시점의 다른 의존성

추상화된 인터페이스와 의존하는 영화 객체는 컴파일시점에서 가격과 퍼센트 할인에 대해서 알지 못한다. 즉 컴파일시점에는 할인이라는 것과 의존하고 있지만 런타임시점에서는 구체적으로 어떠한 할인과 결합하고 있는지 알 수 있게된다.

  • 이러한 특징이 하나의 객체를 다른 객체와 런타임시점에 의존성을 갖게하며 유연한 설계 그리고 다른 클래스들과 협력할 가능성을 열어주기 때문에 변경에 유용한 것이다.

컨텍스트 독립성

객체는 자신과 결합할 구체적인 클래스에 대해 아는 것은 좋지 않다. 구체적은 클래스를 알면 알수록 그 클래스가 사용되는 특정한 문맥에 강하게 결합되기 때문이다.

컨텍스트 독립성이란 클래스가 사용될 특정한 문맥에 대해 최소한의 가정만으로 이루어져 있어 다른 문맥에서 재사용하기가 수월해지는 것을 의미한다

설계가 유연해지기 위해서는 가능한 한 자신이 실행될 컨텍스트에 대한 구체적인 정보를 최대한 적게 알아야 한다. 컨텍스트에 대한 정보가 적으면 적을수록 더 다양한 컨텍스트에서 재사용될 수 있기 때문이다. 결과적으로 설계는 유연해지고 변경에 탄력적으로 대응할 수 있게 된다.

의존성 해결하기

클래스가 실행 컨텍스트에 독립적인데도 어떻게 런타임에 실행 컨텍스트에 적절한 객체들과 협력할 수 있을까?

컴파일타임 의존성을 실행 컨텍스트에 맞는 적절한 런타임 의존성으로 교체하는 것을 의존성 해결이라고 부른다 이러한 의존성 해결은 일반적으로 세 가지 방법을 통해서 해결한다.

  • 객체를 생성하는 시점에 생성자를 통해 의존성 해결
  • 객체 생성 후 setter 메서드를 통해 의존성 해결
    • 객체 생성 이후 Setter를 통해서 의존성을 해결하는 경우 의존성을 주입하기 전까지 불완전한 상태를 유지하고 있다. 이러한 단점을 제거하기 위해서 생성단계에서 의존성을 주입하고 변경되는 경우 의존성을 해결하는 방식을 주로 택한다.
    • 이는 시스템을 안정적으로 유지하면서도 유연성을 향상시킬 수 있기 때문에 의존성 해결을 위해 가장 선호되는 방법이다.
    • 물론 인스턴스 변수로 할인정책을 알 필요없다면 메서드의 인자를 통해서 의존성을 해결할 수도 있다.
  • 메서드 실행 시 인자를 이용해 의존성 해결

바람직한 의존성

객체지향 패러다임의 근간은 협력이다. 객체들은 협력을 통해 어플리케이션에 생명력을 불어넣는다. 따라서 객체간의 의존성은 필수적이며 나쁜 것이 아니다. 하지만 잘못된 의존성은 객체지향의 본질을 훼손한다. 그렇다면 바람직한 의존성을 정하는 기준은 무엇인가 ?

  • 재사용성 어떤 의존성이 다양한 환경에서 클래스를 재사용할 수 없도록 제한한다면 그 의존성은 바람직하지 못한 것이다. 즉 컨텍스트에 독립적인 의존성은 바람직한 의존성이고 특정한 컨텍스트에 강하게 결합된 의존성은 바람직하지 않은 의존성이다. 다시 말해 컴파일 시점에 강하게 결합되는 의존성은 바람직하지 않으며 런타임에 유연하게 변경될 수 있는 의존성은 바람직한 의존성이다.
  • 결합도의 정도는 한 요소가 자신이 의존하고 있는 다른 요소에 대해 알고 있는 정보의 양으로 결정된다. 하나의 요소가 다른 정보에 대해 많이 알고 있을 수록 두 요소는 강하게 결합된다. 더 많이 알수록 더 많이 결합된다
  • 협력을 위해 필요한 정보 이외에는 최대한 숨기는 것이 바람직하다. 추상화에 의존해라

추상화 수준에 따른 의존성

아래로 갈수록 결합도가 느슨해진다.

  • 구체 클래스 의존성
  • 추상 클래스 의존성
  • 인터페이스 의존성

의존성이 명시적이지 않으면 의존성을 파악하기 위해 내부 구현을 직접 살펴볼 수 밖에 없다. 커다란 클래스에 정의된 긴 메서드 내부 어딘가에서 인스턴스를 생성하는 코드를 파악하는 것은 쉽지 않다. 뿐만 아니라 의존성이 명시적이지 않으면 다른 컨텍스트에서 재사용하기 위해 내부 구현을 직접 변경해야 한다.

의존성을 명시적으로 표현해라

new는 해롭다.

new 를 잘못 사용하면 클래스 사이의 결합도가 극단적으로 높아진다. new 가 해로운 이유는 구체클래스에 의존하게 만들기 때문이며 알아야하는 정보의 양을 증가시키기 때문에 궁극적으로 결합도가 높아진다.

  • 구체적인 클래스 이름을 기술해야 하는데, 이런 경우 추상화가 아닌 구체 클래스에 의존할 수 밖에 없기 때문에 결합도가 높아진다.
  • new 연산자는 생성하려는 구체 클래스뿐만 아니라 어떤 인자를 이용해 클래스의 생성자를 호출해야 하는지도 알아야 한다.

물론 New를 통해 생성하는 경우 유용한 경우도 존재한다.

  • 즉 여러가지 정책 중 한가지의 정책이 대부분 사용된다면?
    • 이를 추상화하여 제공하면 클라이언트 코드에서 중복이 많아질 것이다.
    • 이런 경우 생성자를 두개를 두는 방식이나 메서드 별 오버로딩을 통해 이러한 문제를 극복할 수 있다.
      • 즉 자주 사용되는 것을 내부적으로 New로 선언하고
      • 변경될 수 있는 부분에 대한 여지를 다른 생성자나 오버로딩된 메서드를 통해 제공하는 것이다.

모든 의존을 저렇게 열어두어야 하는가?

물론 그렇지 않다. 변경에 대한 영향을 암시하기 때문에 우리는 추상화를 통해 변경에 유연하게 대처하는 방식을 택하고 있다. 하지만 자바 JDK에 포함된 표준 클래스는 자주 변경되는가? 거의 변경될 일이 없고 변경되더라도 우리가 사용하는 부분에 있어서 큰 변화는 존재하지 않는다.

  • 즉 변경이 되는 대상에 대해서는 추상화를 통해 의존성을 명시적으로 받아와 캐스팅하는 것이 바람직하며
  • 자주 변경되지 않는 대상에 대해서는 구체클래스에 의존해도 되는 것이다. 그럼에도 불구하고 ArrayList를 List로 한 번 포장하는 것은 당연히 좋은 습관이다.

요약

어떻게 하는 것이 아니라 무엇을 하는지 를 선언적으로 표현하고 드러냄으로써 유연하고 재사용 가능한 설계를 만든다. 객체 네트워크 행위에 대한 선언적인 정의를 통해서. 어떻게가 아닌 무엇에 집중한다.

설계를 유연하게 만들 수 있었던 이유는 추상화에 의존하고 생성자를 통해 의존성을 명시적으로 드러냈으며 new와 같이 구체 클래스를 직접적으로 다뤄야하는 부분을 외부로 옮겼기 때문이다. 우리는 이제 추상화된 곳에 자식 클래스를 추가함으로써 간단하게 영화가 사용될 컨텍스트를 확장할 수 있다. 결합도를 낮춤으로써 얻게되는 컨텍스트 확장이라는 개념이 유연하고 재사용 가능한 설계를 만드는 핵심인 것이다.

같은 말로

사용과 생성의 책임을 분리하고, 의존성을 생성자에 명시적으로 드러내고, 구체 클래스가 아닌 추상 클래스에 의존하게 함으로써 설계를 유연하게 만들 수 있다. 그리고 그 출발은 객체를 생성하는 책임을 객체 내부가 아니라 클라이언트로 옮기는 것에서 시작했다. 즉 객체 내부적으로 강하게 의존하는 설계가 아니라 클라이언트의 요구 및 변화를 수용할 수 있는 형태로 확장성있게 설계되었다.

0개의 댓글