9.1 종속성 역전

new Thread().start()서버 애플리케이션에서는 ?를 통해 스트림을 만들 수 없다고 말한 적이 있습니다. 컨테이너만 스레드를 생성해야 합니다. 우리는 이제 이 아이디어를 더욱 발전시킬 것입니다.

또한 모든 객체는 컨테이너에 의해서만 생성되어야 합니다 . 물론 모든 개체에 대해 말하는 것이 아니라 소위 비즈니스 개체에 대해 말하는 것입니다. 그것들은 종종 빈(bin)이라고도 합니다. 이 접근 방식의 다리는 클래스를 제거하고 인터페이스로 이동해야 하는 SOLID의 다섯 번째 원칙에서 시작됩니다.

  • 최상위 모듈은 하위 수준 모듈에 의존해서는 안 됩니다. 둘 다 추상화에 의존해야 합니다.
  • 추상화는 세부 사항에 의존해서는 안 됩니다. 구현은 추상화에 의존해야 합니다.

모듈은 특정 구현에 대한 참조를 포함해서는 안 되며 모듈 간의 모든 종속성 및 상호 작용은 추상화(즉, 인터페이스)를 기반으로만 구축되어야 합니다. 이 규칙의 핵심은 한 문구로 작성할 수 있습니다. 모든 종속성은 인터페이스 형식이어야 합니다 .

기본적인 특성과 명백한 단순성에도 불구하고 이 규칙은 가장 자주 위반됩니다. 즉, 프로그램/모듈의 코드에서 new 연산자를 사용하고 특정 유형의 새 객체를 생성할 때마다 인터페이스에 의존하는 대신 구현에 대한 의존성이 형성됩니다.

이것은 피할 수 없으며 어딘가에 개체를 만들어야 한다는 것이 분명합니다. 그러나 최소한 이러한 작업이 수행되고 클래스가 명시적으로 지정된 위치의 수를 최소화하고 이러한 위치를 지역화하고 격리하여 프로그램 코드 전체에 분산되지 않도록 해야 합니다.

매우 좋은 솔루션은 팩토리, 서비스 로케이터, IoC 컨테이너와 같은 특수 객체 및 모듈 내에서 새로운 객체 생성에 집중하는 미친 아이디어입니다.

어떤 의미에서 그러한 결정은 "소프트웨어 시스템이 많은 대안을 지원해야 할 때마다 전체 목록이 시스템의 한 모듈에만 알려져야 한다"는 단일 선택 원칙을 따릅니다 .

따라서 향후에 새로운 옵션(또는 고려 중인 새 개체를 생성하는 경우와 같이 새로운 구현)을 추가해야 하는 경우 이 정보를 포함하는 모듈과 다른 모든 모듈을 업데이트하는 것으로 충분합니다. 영향을 받지 않고 평소처럼 작업을 계속할 수 있습니다.

예 1

. new ArrayList _ List.new()_

예를 들어 컴파일러는 다른 스레드에서 객체에 대한 호출이 있음을 확인하고 거기에 스레드 안전 구현을 배치합니다. 또는 시트 중간에 삽입이 너무 많으면 LinkedList를 기반으로 구현됩니다.

예 2

예를 들어 이것은 정렬에서 이미 발생했습니다. 컬렉션을 정렬하기 위해 정렬 알고리즘을 마지막으로 작성한 것이 언제입니까? 대신 이제 모두가 메서드를 사용 Collections.sort()하고 컬렉션의 요소는 Comparable 인터페이스(comparable)를 지원해야 합니다.

sort()10개 미만의 요소 컬렉션을 메서드에 전달하면 퀵 정렬이 아닌 버블 정렬(버블 정렬)로 정렬하는 것이 가능합니다 .

예 3

컴파일러는 이미 문자열을 연결하는 방법을 보고 있으며 코드를 StringBuilder.append().

9.2 실제 의존성 역전

이제 가장 흥미로운 점은 이론과 실제를 어떻게 결합할 수 있는지 생각해 봅시다. 모듈이 종속성 역전을 위반하지 않고 "종속성"을 올바르게 생성하고 수신하려면 어떻게 해야 합니까?

이렇게 하려면 모듈을 설계할 때 스스로 결정해야 합니다.

  • 모듈이 하는 일, 수행하는 기능
  • 그런 다음 모듈은 환경에서 필요합니다. 즉, 처리해야 할 객체/모듈이 무엇인지;
  • 그리고 그는 그것을 어떻게 얻을 것입니까?

종속성 역전의 원칙을 준수하려면 모듈에서 사용하는 외부 객체와 해당 객체에 대한 참조를 얻는 방법을 확실히 결정해야 합니다.

다음 옵션이 있습니다.

  • 모듈 자체가 객체를 생성합니다.
  • 모듈은 컨테이너에서 개체를 가져옵니다.
  • 모듈은 개체가 어디에서 왔는지 전혀 모릅니다.

문제는 객체를 생성하려면 특정 유형의 생성자를 호출해야 하고 결과적으로 모듈이 인터페이스가 아닌 특정 구현에 종속된다는 것입니다. 그러나 모듈 코드에서 객체를 명시적으로 생성하지 않으려면 Factory Method 패턴을 사용할 수 있습니다 .

"요점은 new를 통해 개체를 직접 인스턴스화하는 대신 클라이언트 클래스에 개체를 생성할 수 있는 일부 인터페이스를 제공한다는 것입니다. 이러한 인터페이스는 항상 올바른 디자인으로 재정의할 수 있기 때문에 저수준 모듈을 사용할 때 어느 정도 유연성을 얻을 수 있습니다. 상위 수준 모듈에서" .

관련 객체의 그룹 또는 패밀리를 생성해야 하는 경우 Factory Method 대신 Abstract factory가 사용됩니다 .

9.3 서비스 로케이터 사용

모듈은 이미 가지고 있는 객체로부터 필요한 객체를 가져옵니다. 시스템에는 모듈이 개체를 "넣고" 저장소에서 개체를 "가져올" 수 있는 일부 개체 저장소가 있다고 가정합니다.

이 접근 방식은 서비스 로케이터 패턴에 의해 구현되며 , 주요 아이디어는 프로그램이 필요할 수 있는 모든 종속성(서비스)을 얻는 방법을 알고 있는 객체를 가지고 있다는 것입니다.

팩토리와의 주요 차이점은 Service Locator가 객체를 생성하지 않지만 실제로는 이미 인스턴스화된 객체를 포함하고 있다는 것입니다(또는 객체를 가져올 위치/방법을 알고 생성하는 경우 첫 번째 호출에서 한 번만). 호출할 때마다 팩토리는 완전한 소유권을 갖고 원하는 대로 무엇이든 할 수 있는 새 객체를 생성합니다.

중요 ! 서비스 로케이터는 이미 존재하는 동일한 개체에 대한 참조를 생성합니다 . 따라서 다른 사람이 동시에 사용할 수 있으므로 Service Locator에서 발급한 개체에 매우 주의해야 합니다.

서비스 로케이터의 개체는 구성 파일을 통해 직접 추가할 수 있으며 실제로 프로그래머에게 편리한 방식으로 추가할 수 있습니다. Service Locator 자체는 정적 메소드 세트, 싱글톤 또는 인터페이스가 있는 정적 클래스일 수 있으며 생성자 또는 메소드를 통해 필요한 클래스로 전달될 수 있습니다.

Service Locator는 때때로 안티 패턴이라고 불리며 권장되지 않습니다(암시적 연결을 생성하고 좋은 디자인의 모양만 제공하기 때문입니다). Mark Seaman에서 자세한 내용을 읽을 수 있습니다.

9.4 의존성 주입

모듈은 "마이닝" 종속성에 대해 전혀 신경 쓰지 않습니다. 작동에 필요한 것만 결정하고 필요한 모든 종속성은 다른 사람이 외부에서 제공(도입)합니다.

이것이 바로 의존성 주입 입니다 . 일반적으로 필요한 종속성은 생성자 매개 변수(생성자 주입) 또는 클래스 메서드(Setter 주입)를 통해 전달됩니다.

이 접근 방식은 종속성 생성 프로세스를 반대로 합니다. 모듈 자체 대신 종속성 생성은 외부의 누군가에 의해 제어됩니다. 객체의 활성 이미 터의 모듈은 수동적이됩니다. 생성하는 사람이 아니라 다른 사람이 그를 위해 생성합니다.

이러한 방향 전환을 제어 역전 또는 할리우드 원칙 이라고 합니다 . "전화하지 마세요. 전화드리겠습니다."

이것은 가장 유연한 솔루션으로 모듈에 최대의 자율성을 부여합니다 . 우리는 모듈만이 "단일 책임 원칙"을 완전히 구현한다고 말할 수 있습니다. 모듈은 다른 것에 대해 걱정하지 않고 작업을 잘 수행하는 데 완전히 집중해야 합니다.

작업에 필요한 모든 것을 모듈에 제공하는 것은 적절한 "전문가"가 처리해야 하는 별도의 작업입니다(일반적으로 특정 컨테이너인 IoC 컨테이너는 종속성 및 해당 구현 관리를 담당합니다).

사실 여기의 모든 것은 삶과 같습니다. 잘 조직 된 회사에서 프로그래머 프로그램, 책상, 컴퓨터 및 업무에 필요한 모든 것은 사무실 관리자가 구입하여 제공합니다. 또는 프로그램의 은유를 생성자로 사용하는 경우 모듈은 와이어에 대해 생각해서는 안되며 부품 자체가 아닌 다른 사람이 생성자를 조립하는 데 관여합니다.

인터페이스를 사용하여 모듈 간의 종속성을 설명하는 것(Dependency Inversion) + 이러한 종속성의 올바른 생성 및 주입(주로 종속성 주입)이 분리를 위한 핵심 기술 이라고 해도 과언이 아닙니다 .

그것들은 코드의 느슨한 결합, 유연성, 변경에 대한 저항, 재사용 및 다른 모든 기술이 거의 의미가 없는 토대 역할을 합니다. 이것은 느슨한 결합과 좋은 아키텍처의 기초입니다.

Inversion of Control의 원칙(Dependency Injection 및 Service Locator와 함께)은 Martin Fowler가 자세히 설명합니다. 그의 두 기사 "Inversion of Control Containers and the Dependency Injection pattern""Inversion of Control" 에 대한 번역이 있습니다 .