거의 모든 사람이 공통 데이터 구조에 액세스해야하는 경우 종속성 주입의 이점은 무엇입니까?


20

OOP에서 글로벌 이 악한 이유는 많이 있습니다 .

공유가 필요한 객체의 수 또는 크기가 너무 커서 함수 매개 변수에서 효율적으로 전달할 수없는 경우 일반적으로 전역 객체 대신 의존성 주입을 권장 합니다.

그러나 거의 모든 사람이 특정 데이터 구조에 대해 알아야 할 경우 의존성 주입이 전역 객체보다 더 나은 이유는 무엇입니까?

(특정 응용 프로그램에서 너무 깊이 탐구하지 않고 일반적으로 요점을 보여주기 위해 단순화 된 것)

유형, 이름, 색상, 속도, 위치 등 수많은 속성과 상태를 가진 수많은 가상 차량이 있습니다. 많은 사용자가 원격으로 제어 할 수 있으며 수많은 이벤트 (사용자- 시작 및 자동)은 많은 상태 또는 속성을 변경할 수 있습니다.

순진한 해결책은 다음과 같이 전역 컨테이너를 만드는 것입니다.

vector<Vehicle> vehicles;

어느 곳에서나 액세스 할 수 있습니다.

보다 OOP 친화적 인 솔루션은 컨테이너를 기본 이벤트 루프를 처리하는 클래스의 멤버로 만들고 생성자에서 인스턴스화하는 것입니다. 그것을 필요로하고 메인 스레드의 멤버 인 모든 클래스에는 생성자의 포인터를 통해 컨테이너에 액세스 할 수 있습니다. 예를 들어, 외부 메시지가 네트워크 연결을 통해 들어 오면 구문 분석을 처리하는 클래스 (각 연결마다 하나씩)가 인계하고 구문 분석기는 포인터 또는 참조를 통해 컨테이너에 액세스 할 수 있습니다. 구문 분석 된 메시지로 인해 컨테이너의 요소가 변경되거나 조치를 수행하기 위해 일부 데이터가 필요한 경우 신호 및 슬롯을 통해 수천 개의 변수를 던질 필요없이 처리 할 수 ​​있습니다. 파서를 호출 한 사람이 나중에 검색 할 수 있도록 파서에 저장). 물론 의존성 주입을 통해 컨테이너에 액세스하는 모든 클래스는 동일한 스레드의 일부입니다. 다른 스레드는 직접 액세스하지 않지만 작업을 수행 한 다음 주 스레드로 신호를 보내면 주 스레드의 슬롯이 컨테이너를 업데이트합니다.

그러나 대부분의 클래스가 컨테이너에 액세스 할 수 있다면 전역과 다른 점은 무엇입니까? 많은 클래스가 컨테이너에 데이터를 필요로하는 경우 "종속성 주입 방식"이 위장 된 전역이 아닌가?

하나의 대답은 스레드 안전성입니다. 전세계 컨테이너를 남용하지 않도록주의하더라도 마감일이 가까워지면 다른 개발자가 글로벌 컨테이너를 다른 스레드에서 사용할 수 있습니다. 충돌 사례. 그러나 의존성 주입의 경우에도 다른 스레드에서 실행중인 누군가에게 포인터를 제공하여 동일한 문제를 일으킬 수 있습니다.


6
잠깐, 당신은 변하지 않는 세계에 대해 이야기하고 그것을 정당화하기 위해 "세계 상태가 왜 나쁜지"에 대한 링크를 사용합니까? WTF?
Telastyn

1
전 세계를 정당화하는 것이 아닙니다. 나는 글로벌이 좋은 해결책이 아니라는 사실에 동의하는 것이 분명하다고 생각합니다. 나는 의존성 주입을 사용하는 것조차도 글로벌보다 훨씬 나쁘지 않거나 심지어 거의 구별 할 수없는 상황에 대해 이야기하고 있습니다. 대답은 여전히이 상황에서 의존성 주입을 사용하는 다른 숨겨진 혜택을 가리킬 수 있도록, 또는 다른 방법은 디보다 더 나은 것이라고
VSZ

9
그러나 읽기 전용 글로벌과 글로벌 상태 에는 결정적인 차이가 있습니다.
Telastyn

1
가능한 중복 복제 또는 의존 팩토리
gnat

1
@vsz not really-문제는 많은 개별 객체의 문제를 해결하는 방법으로 서비스 로케이터 팩토리에 관한 것입니다. 그러나 그 대답은 클래스에 전달되는 전역 데이터가 아닌 클래스가 전역 데이터에 액세스하도록 허용하는 문제에 대한 답변입니다.
gbjbaanb

답변:


37

거의 모든 사람이 특정 데이터 구조에 대해 알아야하는 경우 Dependency Injection이 전역 객체보다 더 나은 이유는 무엇입니까?

의존성 주입은 얇게 썬 빵 이후 가장 좋은 방법 이며, 전역 객체는 수십 년 동안 모든 악의 근원 으로 알려져 왔기 때문에 다소 흥미로운 질문입니다.

의존성 주입의 포인트는 단순히 리소스가 필요한 모든 액터가 리소스를 가질 수 있도록 보장하는 것이 아닙니다. 분명히 모든 리소스를 글로벌로 만들면 모든 액터가 모든 리소스에 액세스 할 수 있기 때문에 문제가 해결 되었습니까?

의존성 주입 시점은 다음과 같습니다.

  1. 행위자가 필요에 따라 자원 접근 할 수 있도록 하고
  2. 주어진 액터가 액세스하는 리소스의 인스턴스 를 제어 합니다.

특정 구성에서 모든 액터가 동일한 자원 인스턴스에 액세스해야한다는 사실은 관련이 없습니다. 날 믿어, 언젠가는 배우가 다른 리소스 인스턴스에 액세스 할 수 있도록 사물을 재구성해야 할 필요가 있으며, 코너에 자신을 그렸다는 것을 알게 될 것입니다. 일부 답변은 이미 그러한 구성을 지적했습니다 : testing .

또 다른 예 : 응용 프로그램을 클라이언트 서버로 분할한다고 가정합니다. 클라이언트의 모든 행위자는 클라이언트에서 동일한 중앙 자원 세트를 사용하고 서버의 모든 행위자는 서버에서 동일한 중앙 자원 세트를 사용합니다. 이제 언젠가 클라이언트와 서버가 단일 실행 파일로 패키지되고 동일한 가상 머신에서 실행되는 "독립형"버전의 클라이언트-서버 애플리케이션을 작성하기로 결정했다고 가정하십시오. (또는 선택한 언어에 따라 런타임 환경)

의존성 주입을 사용하는 경우 모든 클라이언트 행위자에게 작업 할 클라이언트 자원 인스턴스가 제공되도록하는 반면 모든 서버 행위자는 서버 자원 인스턴스를 수신합니다.

의존성 주입을 사용하지 않으면 하나의 가상 머신에 각 자원의 글로벌 인스턴스가 하나만 존재할 수 있으므로 운이 나빠집니다.

그렇다면 모든 액터가 해당 리소스에 실제로 액세스해야합니까? 정말?

그 자원을 신의 대상으로 바꾸는 실수를했거나 (물론 모든 사람이 접근해야 함) 프로젝트에서 실제로 해당 자원에 액세스 해야하는 액터 수를 과대 평가하고있을 수 있습니다. .

글로벌을 사용하면 전체 애플리케이션의 모든 단일 소스 코드 라인이 모든 단일 글로벌 자원에 액세스 할 수 있습니다. 종속성 주입을 사용하면 각 리소스 인스턴스는 실제로 필요한 액터에게만 표시됩니다. 둘이 동일하면 (특정 자원이 필요한 행위자가 프로젝트에서 소스 코드 라인의 100 %를 구성하는 행위자) 설계에서 실수를 한 것입니다. 그래서

  • 큰 거대 신 자원을 작은 하위 자원으로 리팩토링하므로, 다른 배우가 다른 조각에 액세스해야하지만, 배우가 모든 조각을 필요로하는 경우는 거의 없습니다.

  • 액터를 리팩터링하여 작업해야하는 문제의 서브 세트 만 매개 변수로 승인하므로 항상 큰 거대한 중앙 자원을 참조 할 필요가 없습니다.


나는 이것을 이해하지 못한다-한 손으로 DI는 여러 인스턴스의 자원을 사용할 수 있고 전역은 그렇지 않다고 말한다. "전역"은 단일 인스턴스 또는 단일 클래스 여야합니다.
gbjbaanb

3
@gbjbaanb 자원이 Zork이라고 가정하십시오. OP는 자신의 시스템에있는 모든 단일 객체가 작업하기 위해 Zork을 필요로 할뿐만 아니라 하나의 Zork 만 존재할 것이며, 그의 모든 객체가 해당 Zork 인스턴스 하나에 만 액세스하면된다고 말합니다. 나는이 두 가지 가정 중 어느 것도 합리적이지 않다고 말하고있다. 아마도 그의 모든 물체가 Zork을 필요로하는 것은 사실이 아니며, 어떤 물체는 Zork의 특정 인스턴스를 필요로하는 반면 다른 물체는 Zork의 다른 인스턴스.
Mike Nakis

2
@gbjbaanb 나는 Zork의 두 개의 글로벌 인스턴스를 가지고 왜 바보 같은지를 설명하고 ZorkA와 ZorkB로 명명하고 ZorkA를 사용할 객체와 ZorkB를 사용할 객체로 하드 코딩하는 이유를 묻는다고 생각하지 않는다. 권리?
Mike Nakis

1
나는 모든 사람에게 말하지 않았고 거의 모든 사람에게 말했다 . 문제의 핵심은 DI가 그것을 필요로하지 않는 소수의 행위자로부터의 접근을 거부하는 것 외에 다른 이점이 있는지에 대한 것이었다. "신의 대상"은 반 패턴이지만, 맹목적으로 따르는 모범 사례도 될 수 있다는 것을 알고 있습니다. 프로그램의 전체 목적은 특정 자원으로 다양한 작업을 수행하는 것입니다.이 경우 거의 모든 사람이 해당 자원에 액세스해야합니다. 좋은 예는 이미지에서 작동하는 프로그램입니다. 이미지의 거의 모든 것이 이미지 데이터와 관련이 있습니다.
vsz

1
@gbjbaanb 나는 하나의 클라이언트 클래스가 ZorkA 또는 ZorkB에서 독점적으로 작업 할 수 있고 클라이언트 클래스가 어느 클래스를 잡을지 결정하지 않는 것이 좋습니다.
유진 Ryabtsev

39

변경 불가능한 글로벌이 OOP에서 악한 이유는 많이 있습니다.

그것은 모호한 주장입니다. 당신이 증거로 사용하는 링크를 참조 상태 - 가변 전역. 그들은 분명히 악하다. 읽기 전용 전역은 상수입니다. 상수는 제정신입니다. 내 말은, 당신은 모든 수업에 파이의 가치를 주입하지 않을 것입니다.

공유가 필요한 객체의 수 또는 크기가 너무 커서 함수 매개 변수에서 효율적으로 전달할 수없는 경우 일반적으로 전역 객체 대신 의존성 주입을 권장합니다.

아닙니다.

함수 / 클래스에 종속성이 너무 많으면 디자인이 왜 나쁜지 멈추고 살펴 보는 것이 좋습니다. 의존성의 보트로드는 클래스 / 기능이 너무 많은 일을하고 있거나 추상화가 충분하지 않다는 신호입니다.

유형, 이름, 색상, 속도, 위치 등 수많은 속성과 상태를 가진 수많은 가상 차량이 있습니다. 많은 사용자가 원격으로 제어 할 수 있으며 수많은 이벤트 (사용자- 시작 및 자동)은 많은 상태 또는 속성을 변경할 수 있습니다.

이것은 디자인 악몽입니다. 유효성 검사 방식, 동시성 제한 방식없이 사용 / 악용 될 수있는 많은 것들이 있습니다.

그러나 대부분의 클래스가 컨테이너에 액세스 할 수 있다면 전역과 다른 점은 무엇입니까? 많은 클래스가 컨테이너에 데이터를 필요로하는 경우 "종속성 주입 방식"이 위장 된 전역이 아닌가?

예, 어디에서나 공통 데이터에 연결하는 것이 글로벌과 다르지 않으므로 여전히 나쁩니다.

단점은 전역 변수 자체에서 소비자를 분리한다는 것입니다. 이를 통해 인스턴스 작성 방법에 약간의 유연성 이 제공 됩니다. 또한 하나의 인스턴스 만 갖도록 강요 하지 않습니다 . 다른 소비자를 다른 소비자에게 전달할 수 있습니다. 그런 다음 필요한 소비자마다 다른 인터페이스 구현을 작성할 수도 있습니다.

그리고 소프트웨어의 변화하는 요구 사항이 필연적으로 수반되는 변경으로 인해 이러한 기본적인 유연성 수준이 필수적 입니다.


"non-mutable"과 혼동해서 죄송합니다. 나는 mutable이라고 말하고 싶었고 어떻게 든 내 실수를 인식하지 못했습니다.
vsz

"이것은 악몽이다"-나는 알고있다. 그러나 모든 해당 이벤트 데이터의 변경을 수행 할 수 있어야 한다는 요구 사항이있는 경우 이벤트를 추상화 계층 아래에서 분리하고 숨겨도 이벤트가 데이터에 연결됩니다. 전체 프로그램은 이 데이터 에 관한 것입니다. 예를 들어, 프로그램이 이미지에 대해 많은 다른 작업을 수행해야하는 경우, 모든 클래스 또는 거의 모든 클래스보다 이미지 데이터에 연결됩니다. "다른 것을하는 프로그램을 작성하자"는 말은 용납되지 않습니다.
vsz

2
일부 데이터베이스에 일상적으로 액세스하는 많은 개체가있는 응용 프로그램은 전례가 없습니다.
Robert Harvey

@RobertHarvey-확실하지만, 그들이 의존하는 인터페이스는 "데이터베이스에 액세스 할 수 foo있습니까 "또는 " s의 일부 영구 데이터 저장소 "입니까?
Telastyn

1
PHB가 500 개의 기능을 요청하고 Dilbert가 거부 한 Dilbert를 상기시킵니다. 따라서 PHB는 하나의 기능을 요청하고 Dilbert는 말합니다. 다음 날 Dilbert는 1 개의 기능에 대해 각각 500 개의 요청을받습니다. 크기가 조정되지 않으면 어떻게 옷을 입어도 크기가 조정되지 않습니다.
corsiKa

16

고려해야 할 세 가지 주요 이유가 있습니다.

  1. 가독성. 모든 코드 단위가 주입되거나 매개 변수로 전달되는 데 필요한 모든 것이 있으면 코드를 쉽게 확인하고 수행중인 작업을 즉시 확인할 수 있습니다. 이것은 당신에게 기능의 지역성을 제공하며, 또한 관심사를 더 잘 분리하고 생각하게 만듭니다 ...
  2. 모듈성. 파서가 전체 차량 목록과 모든 속성에 대해 알아야하는 이유는 무엇입니까? 아마 그렇지 않습니다. 차량 ID가 있는지 또는 차량 X에 속성 Y가 있는지 여부를 쿼리해야 할 수도 있습니다. 즉, 그렇게하는 서비스를 작성하고 해당 서비스 만 파서에 삽입 할 수 있습니다. 갑자기 모든 코드가 관련 데이터 만 처리하므로 훨씬 더 합리적인 디자인으로 끝납니다. 그러면 우리는 ...
  3. 테스트 가능성. 일반적인 단위 테스트의 경우 다양한 시나리오에 대해 환경을 설정하고 주입하면이를 구현하기가 매우 쉽습니다. 다시, 파서 예제로 돌아가서, 파서에 대해 작성한 모든 테스트 케이스에 대해 본격적인 전차 목록을 항상 직접 제작하고 싶습니까? 아니면 위에서 언급 한 서비스 객체의 모의 구현을 만들어서 다양한 수의 ID를 반환합니까? 어떤 것을 고르는 지 알고 있습니다.

요약하자면, DI는 목표가 아니라 목표를 달성 할 수있는 도구 일뿐입니다. 당신이 그것을 잘못 사용하면 (예시처럼) 목표에 더 가까워지지 않을 것입니다 .DI의 마법 속성은 나쁜 디자인을 좋게 만듭니다.


유지 보수 문제에 중점을 둔 간결한 답변은 +1입니다. 더 많은 P : SE 답변은 이와 같습니다.
dodgethesteamroller

1
당신의 요약은 너무 좋습니다. 잘 했어 [달의 디자인 패턴] 또는 [달의 유행어]가 마술로 문제를 해결한다고 생각하면 시간이 나쁠 것입니다.
corsiKa

@ corsiKa 나는 그 요점을 볼 수 없었기 때문에 오랫동안 DI (또는 Spring,보다 정확하게 말해서)를 싫어했습니다. 그것은 명백한 이익이없는 엄청나게 많은 번거 로움처럼 보였습니다 (XML 구성 시대에 돌아 왔습니다). DI가 실제로 당신을 어떻게 사느냐에 대한 문서를 찾았 으면 좋겠습니다. 그러나 나는하지 않았으므로 스스로 알아 내야했습니다. 오랜 시간이 걸렸습니다. :)
biziclop

3

테스트 가능성에 대한 것입니다-일반적으로 전역 객체는 유지 관리가 쉽고 읽기 쉽고 이해하기 쉽습니다 (물론 일단 알면 이해가 가능합니다).하지만 영구적 인 고정 장치이며 클래스를 격리 된 테스트로 나눌 때 전역 객체가 "나에 대해"라고 말하고 있기 때문에 격리 된 테스트를 수행하려면 전역 객체가 포함되어야합니다. 대부분의 테스트 프레임 워크가이 시나리오를 쉽게 지원하지는 않습니다.

예, 여전히 글로벌입니다. 액세스하는 기본 이유는 변경되지 않았으며 액세스 방식 만 변경되었습니다.

초기화와 관련해서는 여전히 문제가됩니다. 테스트를 실행할 수 있도록 구성을 설정해야하므로 아무 것도 얻지 못합니다. 큰 종속 객체를 여러 개의 작은 객체로 나누고 적절한 객체를 필요한 클래스에 전달할 수 있다고 생각하지만 이점을 능가하기 위해서는 훨씬 더 복잡해질 것입니다.

이 모든 것에 관계없이, '개가 흔들리는 꼬리'의 예인 테스트의 필요성은 코드베이스가 어떻게 구성되어 있는지를 주도하고 있습니다 (현재 날짜 시간과 같은 정적 클래스와 같은 항목에서 비슷한 문제가 발생 함).

개인적으로 나는 모든 전역 데이터를 전역 객체 (또는 여러 객체)에 고정시키는 것을 선호하지만 클래스에 필요한 비트를 얻기 위해 접근자를 제공합니다. 그런 다음 DI를 추가하지 않고도 테스트 할 때 원하는 데이터를 반환하도록 모의 할 수 있습니다.


"일반적으로 전역 객체는 유지 관리가 가능합니다"-변경 가능하지 않은 경우. 내 경험상, 너무 많은 변경 가능한 글로벌 상태를 갖는 것은 악몽 일 수 있습니다 (그러나 그것을 참을 수있는 방법이 있지만).
sleske

3

이미 가지고있는 답변 외에도

유형, 이름, 색상, 속도, 위치 등 다양한 속성과 상태를 가진 수많은 가상 차량이 있습니다. 시작 및 자동)은 많은 상태 또는 속성을 변경할 수 있습니다.

OOP 프로그래밍의 특징 중 하나는 특정 객체에 실제로 필요한 속성 만 유지하는 것입니다.

개체에 속성이 너무 많은 경우 개체를 하위 개체로 세분화해야합니다.

편집하다

전역 객체가 나쁜 이유를 이미 언급 했으므로 이에 대한 예를 하나 추가하겠습니다.

Windows 폼의 GUI 코드에서 단위 테스트를 수행해야합니다. 글로벌을 사용하는 경우 모든 버튼을 만들어야하며 예를 들어 적절한 테스트 사례를 만드는 이벤트입니다.


예를 들어 파서는 모든 것을 변경할 수있는 명령이 수신 될 수 있기 때문에 모든 것을 알아야합니다.
vsz

1
"실제 질문에 오기 전에"... 당신은 그의 질문에 대답 할 것입니까?
gbjbaanb

@gbjbaanb 질문은 많은 텍스트를 얻었습니다. 시간을내어 제대로 읽으십시오.
수학
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.