[오브젝트] 23일차

da__ell·2023년 8월 27일
0

독서 - 오브젝트

목록 보기
23/25
post-thumbnail

p.291 ~ p.304

순수한 가공물에게 책임 할당하기

책임 할당의 가장 기본 원칙은 책임을 수행하는 데 필요한 정보를 가장 많이 알고 있는 정보 전문가(INFORMATION EXPERT)에게 책임을 할당하는 것

도메인 모델은 정보 전문가를 찾기 위해 참조할 수 있는 일차적인 재료 → 책임을 할당하고 싶다면 도메인 모델 안의 개념 중 적절한 후보를 찾아봐야 한다.

크레이그 라만 - 시스템을 객체로 분해하는 2가지 방식

  1. 표현적 분해

    도메인에 존재하는 사물 또는 개념을 표현하는 객체들을 이용해 시스템을 분해하는 것

    도메인 모델에 담겨 있는 개념과 관계를 따르고 도메인과 소프트웨어 사이의 표현적 차이를 최소화 하는 것이 목적이다. → 객체 지향 설계를 위한 가장 기본적인 접근법

    실제 동작하는 애플리케이션은 DB접근을 위한 객체와 같이 도메인 개념들을 초월하는 기계적인 개념이 필요할 수 있다.

  2. 행위적 분해

    모든 책임을 도메인 객체에 할당하면 낮은 응집도, 높은 결합도, 재사용성 저하와 같은 문제가 발생할 수 있다. → 이를 해결하기 위해 설계자가 편의를 위해 임의로 만들어낸 가공의 객체에 책임을 할당 → PURE FABRICATION(순수한 가공물)

    어떤 행동을 추가하려는데 행동을 책임질 마땅한 도메인 개념이 존재하지 않다면 PURE FABRICATION에 추가하고 책임을 할당하라

    설계자로서 도메인 추상화를 기반으로 애플리케이션 로직을 설계하는 동시에 품질의 측면에서 균형을 맞추는 데 필요한 객체들을 창조하여야 한다.

먼저 도메인의 본질적인 개념을 표현하는 추상화를 이용해 애플리케이션을 구축하라. 도메인 개념이 만족스럽지 못하다면 주저않고 인공적인 객체를 창조하라.

6-3. 의존성 주입

사용하는 객체가 아닌 외부의 독립적인 객체가 인스턴스를 생성하고 이를 전달해서 의존성을 해결하는 방법을 의존성 주입(Dependency Injection)이라고 한다.

의존성 주입은 의존성을 해결하기 위해 의존성을 객체의 퍼블릭 인터페이스에 명시적으로 드러내서 외부에서 필요한 런타임 의존성을 전달할 수 있도록 만드는 방법을 포괄하는 명칭이다.

의존성 주입에는 의존성을 해결하는 3가지 방법이 있다.

  1. 생성자 주입 - 객체를 생성하는 시점에 생성자를 통해 해결

  2. setter 주입 - 객체 생성 후 setter 메서드를 통해 의존성 해결

    생성자 주입을 통해 설정된 인스턴스는 객체의 생명주기 전체에 걸쳐 관계를 유지 / setter 메서드는 언제라도 의존 대상을 교체 가능

    setter 주입의 단점은 객체가 올바로 생성되기 위해 어떤 의존성이 필수적인지를 명시적으로 표현할 수 없다는 것 → 객체가 생성된 후에 호출되어야 하기 때문에 setter 메서드 호출을 누락하면 객체는 비정상적인 상태로 생성될 것이다.

  3. 메서드 주입 - 메서드 실행 시 인자를 이용한 의존성 해결

    메서드가 의존성을 필요하는 유일한 경우일 때 사용할 수 있다. 생성자 주입을 통해 의존성을 전달 받으면 객체가 올바른 상태로 생성되는 데 필요한 의존성을 명확하게 표현할 수 있다는 장점이 잇지만 주입된 의존성이 한 두개의 메서드에서만 사용된다면 각 메서드의 인자로 전달하는 것이 더 나은 방법일 수도 있다.

숨겨진 의존성은 나쁘다

의존성 주입 외에도 의존성을 해결할 수 있는 다양한 방법이 존재한다. 가장 널리 사용되는 방법은 SERVICE LOCATOR 패턴이다. SERVICE LOCATOR는 의존성을 해결할 객체들을 보관하는 일종의 저장소 이다.

외부에서 객체에게 의존성을 전달하는 의존성 주입과 다르게 객체가 직접 SERVICE LOCATOR에게 의존성을 해결해줄 것을 요청한다.

SERVICE LOCATOR 패턴은 서비스를 사용하는 코드로부터 서비스가 누구인지(서비스를 구현한 구체 클래스 타입이 무엇인지), 어디에 있는지(클래스 인스턴스를 어떻게 얻을지)를 몰라도 되게 해준다.

SERVICE LOCATOR 패턴의 가장 큰 단점은 의존성을 감춘다는 것이다. 의존성이 암시적이고 깊숙한 곳에 숨겨질 경우 관련된 문제가 컴파일 타임이 아닌 런타임에 가서야 발견된다는 사실이다.

모든 단위 테스트 케이스가 SERVICE LOCATOR의 상태를 공유하게 되기 때문에 각각의 단위 테스트는 서로 고립되어야 한다는 기본 원칙을 위반하는 것이다.

클래스의 퍼블릭 인터페이스만으로 사용 방법을 이해할 수 있는 코드가 캡슐화의 관점에서 훌륭한 코드이다. 클래스의 사용법을 익히기 위해 내부 구현를 확인해야 한다면 캡슐화는 무너진 것이다.

따라서 의존성을 구현 내부로 감추는 SERVICE LOCATOR는 캡슐화를 위반한다.

의존성 주입은 클래스의 퍼블릭 인터페이스에 필요한 의존성을 명시적으로 드러내면서 객체의 캡슐을 보호한다. 의존성과 관련된 문제도 컴파일 타임에 확인할 수 있다. 단위 테스트를 작성할 때도 필요한 인자를 전달해서 필요한 객체를 생성하면 된다.

의존성 주입을 지원하는 프레임워크를 사용하지 못하는 경우나 깊은 호출 계층에 걸쳐 동일한 객체를 계속해서 전달해야 하는 경우에는 SERVICE LOCATOR 패턴을 사용하는 것을 고려하라.

요점은 의존성을 명시적으로 표현할 수 있는 방법을 사용하는 것이다.

6-4. 의존성 역전 원칙

추상화와 의존성 역전

객체 사이의 협력이 존재할 때 그 협력의 본질을 담고 있는 것은 상위 수준의 정책이다.

상위 수준의 클래스가 하위 수준의 클래스에 의존한다면 하위 수준의 변경에 의해 상위 수준 클래스가 영향을 받게 될 것이다. → 상위 수준의 클래스를 재사용할 때 하위 수준의 클래스도 필요하기 때문에 재사용하기 어려워진다.

유연하고 재사용 가능한 설계를 위해서는 모든 의존성의 방향이 추상 클래스나 인터페이스와 같은 추상화를 따라야 한다. 구체 클래스가 의존성의 목적지가 되어서는 안된다.

이 내용이 의존성 원칙이다.

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

의존성 역전 원칙과 패키지

객체지향 프로그래밍 언어에서 어떤 구성요소의 소유권을 결정하는 것은 모듈이다. → 자바는 패키지를 이용해 모듈을 구현한다.

코드의 컴파일이 성공하기 위해 함께 존재해야 하는 코드를 정의하는 것이 바로 컴파일타임 의존성이다.

추상화를 별도의 독립적인 패키지가 아니라 클라이언트가 속한 패키지에 포함시켜야 한다. 따라서 함께 재사용될 필요가 없는 클래스들은 별도의 독립적인 패키지에 모아야 한다. → 마틴 파울러 : SPERATED INTERFACE 패턴

의존성 역전 원칙에 따라 상위수준의 협력 흐름을 재사용하기 위해서는 추상화가 제공하는 인터페이스의 소유권 역시 역전시켜야 한다. 잘 설계된 객체지향 애플리케이션에서는 인터페이스의 소유권을 서버가 아닌 클라이언트에 위치시칸다. 이것이 객체지향 프레임워크의 모듈 구조를 설계하는 데 가장 중요한 핵심 원칙이다.

유연하고 재사용 가능하며 컨텍스트에 독립적인 설계는 전통적인 패러다임이 고수하는 의존성의 방향을 역전시킨다. 객체지향 패러다임에서는 상위 수준 모듈과 하위 수준 모듈이 모두 추상화에 의존하며 인터페이스가 상위 수준 모듈에 속한다.

profile
daelkdev@gmail.com

0개의 댓글