의존성 주입 vs 팩토리 패턴


498

Dependency Injection 사용을 위해 인용 된 대부분의 예제는 팩토리 패턴을 사용하여 해결할 수도 있습니다. 의존성 주입과 팩토리의 차이가 희미하거나 희미합니다.

일단 누군가 당신이 그것을 사용하는 방법이 차이를 만든다고 말해주었습니다!

한 번 문제를 해결하기 위해 DI 컨테이너 인 StructureMap을 사용 했으며 나중에 간단한 팩토리에서 작동하도록 Structure를 다시 디자인하고 StructureMap에 대한 참조를 제거했습니다.

누구든지 그들과의 차이점과 사용 위치, 가장 좋은 방법은 무엇입니까?


21
이 두 가지 접근 방식이 서로 보완 할 수 없는가 : 의존성 주입을 사용하여 팩토리 클래스를 주입합니까?
Richard Ev

20
이 질문에 코드가 들어있는 답변이 있다면 정말 좋을 것입니다! DI가 어떻게 팩토리를 사용하여 창조하는 데 유리하고 다른지 알지 못합니까? 생성되는 obj / implementation을 변경하려면 팩토리 클래스에서 한 줄만 바꾸면됩니까?
기드온

2
@gideon은 앱이나 적어도 팩토리 클래스를 포함하는 모듈을 컴파일하도록 강요하지 않습니까?
lysergic-acid

1
@liortal 맞습니다. 그 의견 이후 DI에 대한 오랜 연구를 해왔으며 이제 DI가 팩토리 방법을 한 단계 앞서가는 것을 이해합니다.
기드온

1
이 위대한 답변을 확인하십시오 : stackoverflow.com/questions/4985455/…- 그는 매우 잘 말하고 코드 샘플을 제공합니다.
Luis Perez

답변:


293

팩토리를 사용할 때 코드는 실제로 객체 생성을 담당합니다. DI는 그 책임을 다른 클래스 나 프레임 워크 (외부 코드와는 별도)로 아웃소싱합니다.


172
DI 패턴에는 프레임 워크가 필요하지 않습니다. DI를 수행하는 팩토리를 수동으로 작성하여 DI를 수행 할 수 있습니다. DI 프레임 워크는 더 쉬워집니다.
Esko Luontola

28
@Perpetualcoder-감사합니다 @Esko-고급 타사 라이브러리를 의미하는 단어 프레임 워크를 따라 가지 마십시오.
willcodejavaforfood

4
@willcode +1 감사합니다! 따라서 대신 config / xml 파일을 변경해야합니다. 내가 참조. 위키 링크 en.wikipedia.org/wiki/...는 같은 공장 패턴을 정의Manually-Injected Dependency
기드온

5
1 줄의 XML을 변경하는 것과 1 줄의 코드를 변경하는 이점을 얻지 못합니다. 좀 더 자세히 설명해 주시겠습니까?
Rafael Eyng

1
"DI"를 "공장"으로 대체하여 OP 답변을 반복하십시오.
Edson Medina

219

개념을 명확하고 단순하게 유지하는 것이 좋습니다. Dependency Injection은 소프트웨어 구성 요소를 느슨하게 연결하기위한 구조적 패턴입니다. 팩토리 패턴은 다른 클래스의 객체를 만드는 책임을 다른 엔터티로 분리하는 한 가지 방법 일뿐입니다. DI를 구현하기위한 툴로 팩토리 패턴을 호출 할 수 있습니다. 의존성 주입은 생성자를 사용하는 DI, XML 파일 매핑 등을 사용하여 DI와 같은 여러 가지 방법으로 구현할 수 있습니다.


4
팩토리 패턴은 Dependency Injection을 구현하는 방법 중 하나입니다. 팩토리 패턴에 의존성 주입을 사용하는 또 다른 이점은 DI 프레임 워크가 구체적인 유형에 대해 추상화를 등록하는 방법의 유연성을 제공한다는 것입니다. 구성, XML 또는 자동 구성과 같은 코드 이러한 유연성 덕분에 객체 수명을 관리하거나 인터페이스 등록 방법 및시기를 결정할 수 있습니다.
Teoman shipahi

1
팩토리 패턴은 DI보다 높은 수준의 추상화를 제공합니다. 공장은 DI와 마찬가지로 구성 옵션을 제공 할 수 있지만, 모든 구성 세부 사항을 숨기도록 선택할 수 있으므로 공장은 클라이언트가 모르게 완전히 다른 구현으로 전환하기로 결정할 수 있습니다. DI 자체로는 이것을 허용하지 않습니다. DI는 고객이 원하는 변경 사항을 지정하도록 요구합니다.
neuron

1
좋은 대답입니다. 많은 사람들이 그 질문에 혼동하고 있습니다. "어떤 패튼을 선택해야합니까?" 실제로 디자인 패턴은 적절한 상황에서 문제를 해결하는 방법 일뿐입니다. 어떤 디자인 패턴을 사용해야하는지 모르거나 "패턴을 어디에 구현해야합니까?" 이때는 필요하지 않습니다.
Benyi

2
Factory Pattern은 DI가 아닌 IoC를 구현하는 도구라고 말하는 것이 더 정확하다고 생각합니다. 주의하시기 바랍니다 DI는 IOC의의 한 형태이다
Hooman Bahreini가

185

의존성 주입

부품 자체를 인스턴스화하는 대신 자동차 작동해야하는 부품을 요구합니다.

class Car
{
    private Engine engine;
    private SteeringWheel wheel;
    private Tires tires;

    public Car(Engine engine, SteeringWheel wheel, Tires tires)
    {
        this.engine = engine;
        this.wheel = wheel;
        this.tires = tires;
    }
}

공장

완전한 객체를 만들기 위해 조각을 모으고 호출자로부터 콘크리트 유형을 숨 깁니다.

static class CarFactory
{
    public ICar BuildCar()
    {
        Engine engine = new Engine();
        SteeringWheel steeringWheel = new SteeringWheel();
        Tires tires = new Tires();
        ICar car = new RaceCar(engine, steeringWheel, tires);
        return car;
    }   
}

결과

보시다시피, 공장과 DI는 서로를 보완합니다.

static void Main()
{
     ICar car = CarFactory.BuildCar();
     // use car
}

당신은 goldilocks와 세 곰을 기억하십니까? 의존성 주입은 그런 식입니다. 동일한 작업을 수행하는 세 가지 방법이 있습니다.

void RaceCar() // example #1
{
    ICar car = CarFactory.BuildCar();
    car.Race();
}

void RaceCar(ICarFactory carFactory) // example #2
{
    ICar car = carFactory.BuildCar();
    car.Race();
}

void RaceCar(ICar car) // example #3
{
    car.Race();
}

예제 # 1- 종속성을 완전히 숨기므로 최악입니다. 이 방법을 블랙 박스로 본다면 자동차가 필요하다는 것을 알 수 없습니다.

예 # 2- 자동차 공장에 들어서서 자동차가 필요하다는 것을 알았으므로 조금 더 좋습니다. 그러나 이번에는 실제로 필요한 모든 방법이 자동차이기 때문에 너무 많이 전달됩니다. 우리는 자동차가 방법 외부에 건설되어 통과 할 수있을 때 자동차를 만들기 위해 공장을 통과하고 있습니다.

예제 # 3- 메소드가 필요한 것을 정확하게 요구하기 때문에 이상적입니다 . 너무 많거나 적지 않습니다. MockCars를 만들기 위해 MockCarFactory를 작성할 필요는 없습니다. 모의를 바로 전달할 수 있습니다. 직접적이고 인터페이스가 거짓말을하지 않습니다.

Misko Hevery의이 Google Tech Talk는 놀랍고 필자의 모범을 바탕으로 한 것입니다. http://www.youtube.com/watch?v=XcT4yYu_TTs


1
공장 패턴은 DI로 주입됩니다.
Mangesh Pimpalkar

7
@PhilGoetz 설명하는 것은 Service Locator 패턴과 비슷합니다. 그들은 소비자와 서비스를 분리한다는 목표에서 비슷한 목표를 가지고 있습니다. 그러나 서비스 로케이터 패턴에는 많은 단점이 있습니다. 주로 의존성을 숨기는 것은 좋지 않습니다 . 소비자는 여전히 의존성을 가져야하지만 명확한 인터페이스를 정의하는 대신 '비밀'로 전달되어 전역 상태에 의존합니다. 이 예에서 소비자는 공장에 대해 전혀 모릅니다. 필요한 것을 요구하기 만하면됩니다.
Despertar

1
@MatthewWhited 대부분의 설계 결정에는 균형이 있습니다. DI를 사용하면 유연성, 투명성 및보다 테스트 가능한 시스템을 얻을 수 있지만 용기를 노출해야합니다. 많은 사람들이 이것을 받아 들일 수있는 거래라고 생각합니다. DI가 항상 최선의 해결책이라고 말하는 것은 아니며,이 질문은 그것에 관한 것이 아닙니다. 이 예제는 각각의 좋은 사용법과 나쁜 사용법을 보여줌으로써 DI와 공장 패턴의 차이를 보여주기위한 것입니다.
Despertar

3
이것은 DI 패턴이 아니며 IoC의 구현 일뿐입니다. DI는 ServiceLocator를 사용하여 올바른 종속성을 결정하고 객체 생성 중에 생성자에 삽입합니다. 코드는 클래스 생성과 객체 생성 만 분리합니다.
Diego Mendes

3
@DiegoMendes 설명하는 것은 DI를 자동화하는 프레임 워크입니다. 호출 할 때 ServiceLocator가 DI를 대신합니다. 내가 보여주는 것은 멋진 프레임 워크가 필요없는 패턴 자체입니다. 참조 stackoverflow.com/a/140655/1160036를 , 또는 참조 en.wikipedia.org/wiki/...을 "[프레임 워크 목록] 지원 의존성 주입하지만, 의존성 주입을 할 필요가 없습니다." 또한 "주입은 종속 개체에 대한 종속성의 전달"입니다. 당신은 아름답고 단순한 개념을 복잡하게 만듭니다.
Despertar

50

DI (Dependency Injection)와 팩토리 패턴이 유사한 이유는 소프트웨어 아키텍처 인 IoC (Inversion of Control)의 두 가지 구현이기 때문입니다. 간단히 말해서 그들은 같은 문제에 대한 두 가지 해결책입니다.

질문에 대답하기 위해 팩토리 패턴과 DI의 주요 차이점은 객체 참조를 얻는 방법입니다. 이름이 암시하는 것처럼 의존성 주입을 사용하면 참조가 주입되거나 코드에 제공됩니다. 팩토리 패턴을 사용하면 코드에서 객체를 가져 오도록 코드에서 참조를 요청해야합니다. 두 구현 모두 코드와 코드에서 사용하는 기본 클래스 또는 객체 참조 유형 간의 연결을 제거하거나 분리합니다.

팩토리 패턴 (또는 실제로 오브젝트 참조를 리턴하는 새 팩토리를 리턴하는 팩토리 인 추상 팩토리 패턴)은 런타임시 요청되는 오브젝트의 유형 또는 클래스에 동적으로 선택하거나 링크하도록 작성 될 수 있습니다. 이것은 IoC의 또 다른 구현 인 Service Locator 패턴과 매우 유사합니다 (DI보다 훨씬 더).

팩토리 디자인 패턴은 상당히 오래되었으며 (소프트웨어 측면에서) 오랫동안 사용되었습니다. 최근 건축 패턴 IoC의 인기가 높아지면서 다시 부활하고 있습니다.

IoC 디자인 패턴과 관련하여 인젝터가 주입되고 로케이터가 배치되며 공장이 리팩터링되었습니다.


3
이것은 최고의 답변입니다 ... 다른 답변은 IoC를 언급하지 않거나 DI가 IoC의 한 형태라는 것을 인식하지 못합니다.
Hooman Bahreini

고마워, IOC를 처음 연구했을 때 나는 이것이 단지 다른 형태의 팩토리 패턴이라고 비명을 질렀다. 그것들은 실제로 서로 비슷하다는 것이 밝혀졌다
Harvey Lin

40

의존성 주입으로 쉽게 해결할 수있는 문제가 있는데, 이는 일련의 공장에서는 쉽게 해결할 수 없습니다.

한편으로 제어 및 의존성 주입 (IOC / DI)의 역전과 서비스 로케이터 또는 공장 (공장)의 차이점은 다음과 같습니다.

IOC / DI는 도메인 객체와 서비스 자체의 완전한 생태계입니다. 지정한 방식으로 모든 것을 설정합니다. 도메인 객체와 서비스는 컨테이너에 의해 구성되어, 자신을 구성하지 않는다 : 그들은 그러므로이없는 모든 컨테이너에 또는 공장에 종속. IOC / DI는 애플리케이션의 최상위 계층 (GUI, 웹 프론트 엔드)에 단일 구성 (컨테이너 구성)으로 모든 구성이 가능한 매우 높은 수준의 구성 성을 허용합니다.

팩토리는 도메인 객체 및 서비스 구성의 일부를 추상화합니다. 그러나 도메인 객체와 서비스는 여전히 자신을 구성하는 방법과 의존하는 모든 것을 얻는 방법을 알아낼 책임이 있습니다. 이러한 "활성"종속성은 응용 프로그램의 모든 계층을 통해 항상 필터링됩니다. 모든 것을 구성 할 단일 장소는 없습니다.


26

DI의 한 가지 단점은 논리로 개체를 초기화 할 수 없다는 것입니다. 예를 들어, 임의의 이름과 연령을 가진 문자를 만들어야하는 경우 DI는 공장 패턴을 선택하는 것이 아닙니다. 팩토리를 사용하면 개체 생성에서 임의 알고리즘을 쉽게 캡슐화 할 수 있습니다.이 알고리즘은 "변화하는 요소 캡슐화"라는 디자인 패턴 중 하나를 지원합니다.


22

수명주기 관리는 인스턴스화 및 주입 외에도 종속성 컨테이너가 담당하는 책임 중 하나입니다. 컨테이너가 인스턴스화 후 구성 요소에 대한 참조를 유지한다는 사실은 팩토리가 아니라 "컨테이너"라고하는 이유입니다. 의존성 주입 컨테이너는 일반적으로 수명주기를 관리하는 데 필요한 객체 또는 싱글 톤 또는 플라이 웨이트와 같은 향후 주입에 재사용되는 객체 만 참조합니다. 컨테이너를 호출 할 때마다 일부 구성 요소의 새 인스턴스를 작성하도록 구성된 경우 컨테이너는 일반적으로 작성된 오브젝트를 잊어 버립니다.

보낸 사람 : http://tutorials.jenkov.com/dependency-injection/dependency-injection-containers.html


15

DI는 공장의 추상화 계층 유형이라고 생각하지만 추상화 이상의 이점을 제공합니다. 진정한 팩토리는 단일 유형을 인스턴스화하고 구성하는 방법을 알고 있습니다. 우수한 DI 계층은 구성을 통해 여러 유형을 인스턴스화하고 구성 할 수있는 기능을 제공합니다.

건설에서 비교적 안정적인 비즈니스 로직을 요구하는 몇 가지 간단한 유형의 프로젝트의 경우, 공장 패턴은 이해하기 쉽고 구현하기 쉬우 며 잘 작동합니다.

OTOH, 구현이 자주 변경 될 것으로 예상되는 수많은 유형을 포함하는 프로젝트가있는 경우 DI는 구성을 통해 런타임시 팩토리를 다시 컴파일하지 않고도이를 수행 할 수있는 유연성을 제공합니다.


14

이론

고려해야 할 두 가지 중요한 사항이 있습니다.

  1. 객체를 만드는 사람

    • [공장] : 어떻게 객체를 작성해야하는지 작성해야합니다. 작성 로직을 포함하는 별도의 팩토리 클래스가 있습니다.
    • [종속성 주입] : 실제 경우에는 외부 프레임 워크 (예 : spring / ejb / guice 인 Java)에 의해 수행됩니다. 주입은 새로운 객체를 명시 적으로 만들지 않고 "마 법적으로"일어난다
  2. 어떤 종류의 개체를 관리합니까?

    • [Factory] : 일반적으로 상태 저장 객체 생성을 담당합니다.
    • [종속성 주입] 상태 비 저장 개체 생성 가능성

단일 프로젝트에서 팩토리 및 종속성 주입을 모두 사용하는 방법에 대한 실제 예

  1. 우리가 만들고 싶은 것

orderline이라는 여러 항목이 포함 된 주문을 작성하기위한 애플리케이션 모듈.

  1. 건축물

다음과 같은 레이어 아키텍처를 생성한다고 가정 해 봅시다.

여기에 이미지 설명을 입력하십시오

도메인 객체는 데이터베이스 내부에 저장된 객체 일 수 있습니다. 리포지토리 (DAO)는 데이터베이스에서 개체를 검색하는 데 도움이됩니다. 서비스는 다른 모듈에 API를 제공합니다. order모듈 에서 작동 가능

  1. 도메인 레이어 및 공장 사용

데이터베이스에있을 엔티티는 Order 및 OrderLine입니다. 주문에는 여러 개의 주문 라인이있을 수 있습니다. 주문과 주문 라인의 관계

이제 중요한 디자인 부분이 있습니다. 이 모듈 이외의 모듈은 자체적으로 OrderLine을 작성하고 관리해야합니까? 주문 라인은 주문과 관련된 경우에만 존재해야합니다. 내부 구현을 외부 클래스에 숨길 수 있다면 가장 좋습니다.

그러나 OrderLines에 대한 지식없이 Order를 작성하는 방법은 무엇입니까?

공장

새로운 주문을 생성하려는 사람은 OrderFactory를 사용했습니다 (주문 생성 방법에 대한 세부 사항은 숨겨 짐).

여기에 이미지 설명을 입력하십시오

그것이 IDE 내부에서 어떻게 보일지입니다. domain패키지 외부의 클래스 는 OrderFactory내부의 생성자 대신 사용 합니다.Order

  1. 종속성 주입 종속성 주입은 리포지토리 및 서비스와 같은 상태 비 저장 계층에서 더 일반적으로 사용됩니다.

OrderRepository 및 OrderService는 종속성 주입 프레임 워크에 의해 관리됩니다. 리포지토리는 데이터베이스에서 CRUD 작업을 관리합니다. 서비스는 리포지토리를 주입하고이를 사용하여 올바른 도메인 클래스를 저장 / 찾기합니다.

여기에 이미지 설명을 입력하십시오


이것을 명확히 해 주시겠습니까? [Factory]: Usually responsible for creation of stateful objects [Dependency Injections] More likely to create stateless objects이유를 모르겠습니다
Maksim Dmitriev

2
상태 저장 개체는 데이터를 데이터베이스에 매핑하는 응용 프로그램의 도메인 계층에있을 가능성이 높으며 일반적으로 종속성 주입을 사용하지 않습니다. 팩토리는 복잡한 객체의 트리에서 AggregateRoot를 만드는 데 자주 사용됩니다
Marcin Szymczak

12

나는이 질문이 오래되었다는 것을 알고 있지만 5 센트를 추가하고 싶습니다.

의존성 주입 (DI)은 여러 가지 방법으로 구성 가능한 팩토리 패턴 (FP)과 같으며 DI와 관련하여 할 수있는 모든 것은 그러한 팩토리로 할 수 있다고 생각합니다.

실제로 스프링을 사용하는 경우 자동 배선 리소스 (DI) 또는 다음과 같은 작업을 수행하는 옵션이 있습니다.

MyBean mb = ctx.getBean("myBean");

그런 다음 'mb'인스턴스를 사용하여 무엇이든하십시오. 인스턴스를 반환하는 팩토리에 대한 호출이 아닙니까?

대부분의 FP 예제에서 유일하게 눈에 띄는 차이점은 XML 또는 다른 클래스에 "myBean"이 무엇인지 구성 할 수 있으며 프레임 워크는 팩토리로 작동하지만 그 외의 다른 것은 동일하다는 것입니다. 확실히 설정 파일을 읽거나 필요에 따라 구현을 가져 오는 팩토리를 가질 수 있습니다.

그리고 당신이 저의 의견을 물어 보면 (그리고 당신이하지 않았다는 것을 알고 있습니다), DI가 똑같은 일을하지만 개발에 더 복잡성을 더한다고 믿습니다. 왜 그렇습니까?

한 가지, DI로 autowire하는 모든 bean에 사용되는 구현을 알기 위해서는 구성 자체로 가야합니다.

그러나 ... 사용중인 객체의 구현을 알 필요가 없다는 약속은 어떻습니까? pfft! 진심으로? 이런 접근 방식을 사용할 때 ... 구현을 쓰는 것과 동일하지 않습니까? 그리고 당신이하지 않더라도 구현이 수행 해야하는 일을 수행하는 방법을 거의 항상 보지 않습니까?

그리고 마지막 한가지를 들어, 그것은 DI 프레임 워크의 약속이 얼마나 중요하지 않습니다 당신이 일 만들 것이다 당신을 분리 당신이 모든 것을 구축 프레임 워크를 사용하는 경우, 자신의 클래스에 종속되지 않는, 그것에서이 그것을 아루 당신이있는 경우 접근 방식이나 프레임 워크를 변경하는 것은 쉬운 일이 아닙니다 ... ...하지만 비즈니스에 가장 적합한 솔루션이 무엇인지 걱정하지 않고 특정 프레임 워크 주변의 모든 것을 구현하기 때문에 그렇게 할 때 문제가 발생할 수 있습니다.

사실, 내가 볼 수있는 FP 또는 DI 접근법의 유일한 실제 비즈니스 응용 프로그램은 런타임 에 사용되는 구현을 변경 해야하는 경우 이지만 적어도 내가 알고있는 프레임 워크는 허용하지 않으므로 떠나야합니다. 다른 접근 방식을 사용해야하는 경우 개발 시점의 구성에서 모든 것이 완벽합니다.

따라서 동일한 응용 프로그램에서 두 범위에서 다르게 수행되는 클래스가있는 경우 (예 : 지주 회사 2 개) 프레임 워크를 구성하여 두 개의 다른 Bean을 만들고 각 코드를 사용하도록 코드를 조정해야합니다. 내가 이런 식으로 쓰는 것과 같지 않습니까?

MyBean mb = MyBeanForEntreprise1(); //In the classes of the first enterprise
MyBean mb = MyBeanForEntreprise2(); //In the classes of the second enterprise

이것과 동일

@Autowired MyBean mbForEnterprise1; //In the classes of the first enterprise
@Autowired MyBean mbForEnterprise2; //In the classes of the second enterprise

이:

MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise1"); //In the classes of the first enterprise
MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise2"); //In the classes of the second enterprise

어쨌든 클래스 또는 구성 파일이든 응용 프로그램에서 무언가를 변경해야하지만 재배치해야합니다.

이런 식으로하는 것이 좋지 않습니까?

MyBean mb = (MyBean)MyFactory.get("mb"); 

그런 식으로, 로깅 된 사용자 엔터프라이즈에 따라 런타임에 올바른 구현을 얻도록 팩토리 코드를 설정 ?? 이제 도움이 될 것입니다. 새 클래스로 새 jar을 추가하고 런타임에도 규칙을 설정할 수도 있습니다 (또는이 옵션을 열어 둔 경우 새 구성 파일 추가). 기존 클래스를 변경하지 않아도됩니다. 이것은 다이나믹 팩토리입니다!

각 엔터프라이즈에 대해 두 가지 구성을 작성하는 것보다 더 도움이되지 않을 수도 있고 각 응용 프로그램에 대해 두 개의 다른 응용 프로그램을 사용하는 것보다 도움이되지 않습니까?

런타임에 전환 할 필요가 없으므로 앱을 구성하고 클래스를 상속하거나 다른 구현을 사용하는 경우 구성을 변경하고 재배치하면됩니다. 공장에서도 가능합니다. 솔직히 말해서 몇 번이나이 일을합니까? 회사의 다른 곳에서 사용되는 앱이 있고 코드를 다른 팀에 전달할 때만 이와 같은 작업을 수행 할 수 있습니다. 그러나 공장에서도 할 수 있으며 역동적 인 공장에서는 더 좋을 것입니다 !!

어쨌든, 댓글 섹션을 열면 나를 죽일 수 있습니다.


3
너무 많은 개발자들이 Dependency Injection 프레임 워크가 새로운 것을 가져 오기 위해 만들어 졌다고 생각합니다. 잘 설명 하듯이, 전통적인 팩토리 (가장 추상적 인 팩토리)는 종속성 반전의 역할을 동일하게 수행 할 수 있습니다. 반전 대 주입? 저에게 의존성 주입 프레임 워크의 유일한 이점은 의존성이 추가 / 변경 될 때 코드를 변경하거나 다시 컴파일하지 않아도된다는 것입니다 (가장 자주 스프링과 같이 XML로 구성 가능하기 때문에). 재 컴파일을 피해야하는 이유는 무엇입니까? 의존성 / 팩토리를 변경하면서 잠재적 인적 오류를 피하기 위해. 그러나 훌륭한 IDE를 사용하면 리팩토링이 잘 작동합니다.
Mik378

6

IOC는 두 가지 방법으로 구현되는 개념입니다. 의존성 생성과 의존성 주입, Factory / Abstract factory는 의존성 생성의 예입니다. 의존성 주입은 생성자, 설정자 및 인터페이스입니다. IOC의 핵심은 구체적인 클래스에 의존하는 것이 아니라 메소드의 추상 (인터페이스 / 추상 클래스)을 정의하고 그 추상을 사용하여 구체적인 클래스의 메소드를 호출하는 것입니다. 팩토리 패턴처럼 기본 클래스 또는 인터페이스를 반환합니다. 유사 의존성 주입은 기본 클래스 / 인터페이스를 사용하여 객체의 값을 설정합니다.


4

의존성 주입을 사용하면 클라이언트는 자신의 의존성을 얻을 필요가 없으며 미리 준비되어 있습니다.

공장에서는 누군가가 생성 된 객체를 필요한 곳으로 가져 오기 위해 전화를 걸어야합니다.

차이점은 주로 팩토리를 호출하고 생성 된 객체를 가져 오는이 한 줄에 있습니다.

그러나 공장에서는이 객체가 필요한 모든 곳 에서이 1 줄을 작성해야합니다. DI를 사용하면 배선 (사용과 생성 된 객체 사이의 관계)을 한 번만 만들고 나중에 사방에 객체의 존재에 의존해야합니다. 다른 한편으로, DI는 종종 준비 측면에서 약간 더 많은 (프레임 워크에 따라 다름) 작업이 필요합니다.


3

DI에 대해 읽고이 게시물에서 끝나 자마자 같은 질문을했습니다. 마지막으로 이것이 내가 이해 한 것이지만 틀린 경우 수정하십시오.

"오래 전에 자체 통치 규칙을 기반으로 결정을 내리고 관리하는 자체 통치 기관이있는 작은 왕국이있었습니다. 나중에는 하나의 규칙 (헌법)이 있고 법원을 통해 시행되는이 작은 통치 조직을 모두 제거하는 큰 정부를 구성했습니다."

작은 왕국의 통치 체는 "공장"입니다

큰 정부는 "의존성 인젝터"입니다.


1
그렇다면 이전의 작은 정부가 지금 커질 때를 언제 정리해야합니까? 무슨 측정으로?
죄수 제로

3

실제 예에서 두 가지 (및 다른) 접근법을 비교하기 위해이 링크 를 살펴볼 수 있습니다 .

기본적으로 요구 사항이 변경되면 DI 대신 팩토리를 사용하면 더 많은 코드를 수정하게됩니다.

이것은 수동 DI에서도 유효합니다 (즉, 객체에 의존성을 제공하는 외부 프레임 워크가 없지만 각 생성자에서 전달합니다).


3

여기의 대부분의 답변은 개념적 차이와 구현 세부 사항을 설명합니다. 그러나 IMO가 가장 중요하고 OP가 요구 한 응용 프로그램의 차이점에 대한 설명을 찾을 수 없었습니다. 이 주제를 다시 열겠습니다.

일단 누군가 당신이 그것을 사용하는 방법이 차이를 만든다고 말해주었습니다!

바로 그거죠. 90 %의 경우 Factory 또는 DI를 사용하여 객체 참조를 얻을 수 있으며 일반적으로 후자를 사용합니다. 다른 10 %의 경우 Factory를 사용하는 것이 올바른 방법 일뿐 입니다. 이러한 경우에는 런타임 매개 변수에서 변수로 오브젝트를 얻는 것이 포함됩니다. 이처럼 :

IWebClient client = factoryWithCache.GetWebClient(url: "stackoverflow.com",
        useCookies: false, connectionTimeout: 120);

이 경우 clientDI에서 가져올 수 없습니다 (적어도 추악한 해결 방법이 필요합니다). 따라서 결정을 내리는 일반적인 규칙 : 런타임 계산 매개 변수없이 종속성을 얻을 수있는 경우 DI가 선호되며 그렇지 않으면 팩토리를 사용하십시오.


2

DI는 Bean을 구성하거나 인스턴스화하는 방법이라고 생각합니다. DI는 생성자, setter-getter 등 여러 가지 방법으로 수행 할 수 있습니다.

팩토리 패턴은 콩을 인스턴스화하는 또 다른 방법입니다. 이 패턴은 팩토리 디자인 패턴을 사용하여 오브젝트를 작성해야 할 때 주로 사용됩니다.이 패턴을 사용하는 동안 Bean의 특성을 구성하지 않고 오브젝트를 인스턴스화하기 때문입니다.

이 링크 확인 : 의존성 주입


2

비노이,

나는 당신이 다른 것을 선택해야한다고 생각하지 않습니다.

종속 클래스 또는 인터페이스를 클래스 생성자 또는 설정 자로 이동하는 동작은 DI 패턴을 따릅니다. 생성자 또는 집합에 전달하는 객체는 Factory를 사용하여 구현할 수 있습니다.

언제 사용합니까? 개발자 조타실에있는 패턴을 사용하십시오. 무엇을 가장 편하게 느끼고 이해하기 쉬운가?


2

나는 3 가지 중요한 측면이 객체와 그 사용법을 지배한다고 믿는다 :
1. 인스턴스화 (있을 경우 초기화와 함께 클래스의).
2. 필요한 경우 주입 (그러한 인스턴스의 주입 ).
3. (생성 된 인스턴스의) 수명주기 관리 .

팩토리 패턴을 사용하면 첫 번째 측면 (인스턴스화)이 달성되지만 나머지 두 가지 측면에 의문이 생깁니다. 다른 인스턴스를 사용하는 클래스 는 팩토리하드 코딩 해야 (인스턴스가 아닌) 커플 링 기능이 느슨해집니다. 게다가, 라이프 사이클 관리팩토리가 여러 장소에서 사용되는 대규모 애플리케이션에서 인스턴스 수는 문제가됩니다 (특히 팩토리가 반환하는 인스턴스의 수명주기를 관리하지 않으면 추악합니다).

반면에 DI (IoC 패턴)를 사용하면 3 개 모두 코드 외부에서 DI 컨테이너로 추상화되며 관리 Bean은이 복잡성에 대해 아무 것도 필요하지 않습니다. 매우 중요한 건축 목표 인 Loose Coupling 은 편안하게 조용히 달성 할 수 있습니다. 또 다른 중요한 건축 목표 인 우려 분리는 공장보다 훨씬 우수합니다.

공장은 소규모 애플리케이션에 적합 할 수 있지만, 공장은 공장보다 DI를 선택하는 것이 좋습니다.


1

내 생각:

Dependecy Injection : 공동 작업자를 매개 변수로 생성자에게 전달합니다. Dependency Injection Framework : 생성자에 매개 변수로 전달할 오브젝트를 작성하기위한 일반 구성 가능한 팩토리.


1
그렇습니다. 이 Q & A에서 DI (Dependency Injection)에 대한 거의 모든 언급은이 정의와 달리이 용어를 오용합니다. 의존성 주입 컨테이너 (DIC)는 객체를 생성하기 위해 일반적이고 구성 가능한 팩토리 역할을하는 가장 일반적인 프레임 워크입니다.
CXJ

1

사출 프레임 워크는 팩토리 패턴의 구현입니다.

그것은 모두 귀하의 요구 사항에 달려 있습니다. 응용 프로그램에서 팩토리 패턴을 구현해야하는 경우 무수한 주입 프레임 워크 구현 중 하나가 요구 사항을 충족 할 가능성이 높습니다.

타사 프레임 워크에서 요구 사항을 충족 할 수없는 경우에만 자체 솔루션을 배포해야합니다. 더 많은 코드를 작성할수록 더 많은 코드를 유지해야합니다. 코드는 자산이 아닌 책임입니다.

어떤 구현을 사용해야하는지에 대한 인수는 애플리케이션의 아키텍처 요구를 이해하는 것만 큼 근본적으로 중요하지 않습니다.


1

공장 디자인 패턴

공장 디자인 패턴은 다음과 같은 특징이 있습니다.

  • 인터페이스
  • 구현 클래스
  • 공장

다음과 같이 스스로 질문 할 때 몇 가지 사항을 관찰 할 수 있습니다

  • 팩토리는 언제 구현 클래스에 대한 객체 (런타임 또는 컴파일 타임)를 생성합니까?
  • 런타임에 구현을 전환하려면 어떻게합니까? - 불가능

이들은 의존성 주입에 의해 처리됩니다.

의존성 주입

의존성을 주입 할 수있는 다른 방법이있을 수 있습니다. 단순화를 위해 인터페이스 인젝션을 사용할 수 있습니다.

DI에서는 컨테이너가 필요한 인스턴스를 만들어 개체에 "주입"합니다.

따라서 정적 인스턴스화가 제거됩니다.

예:

public class MyClass{

  MyInterface find= null;

  //Constructor- During the object instantiation

  public MyClass(MyInterface myInterface ) {

       find = myInterface ;
  }

  public void myMethod(){

       find.doSomething();

  }
}

1

액면가에서 그들은 동일하게 보입니다.

아주 간단한 용어 인 팩토리 패턴 (Creatingal Pattern)은 "객체를 만들기위한 인터페이스 정의"라는 객체를 만드는 데 도움이됩니다. 키 값 종류의 객체 풀 (예 : 사전)이 있고 키를 팩토리에 전달하면 (간단한 팩토리 패턴을 참조 함) 유형을 해결할 수 있습니다. 작업 완료! 반면 의존성 주입 프레임 워크 (예 : Structure Map, Ninject, Unity ... 등)는 같은 일을하는 것으로 보입니다.

하지만 ... "바퀴를 재발 명하지 마십시오"

건축 적 관점에서 볼 때 바인딩 레이어와 "바퀴를 재발 명하지 마십시오".

엔터프라이즈 급 응용 프로그램의 경우 DI의 개념은 종속성을 정의하는 아키텍처 계층에 가깝습니다. 이것을 더 단순화하기 위해 이것을 의존성 해결을 수행하는 별도의 클래스 라이브러리 프로젝트로 생각할 수 있습니다. 주요 응용 프로그램은 Dependency resolver가 다른 구체적인 구현 및 종속성 해결을 참조하는이 프로젝트에 의존합니다.

팩토리에서 "GetType / Create"에 추가 할 때, 더 많은 기능 (XML을 사용하여 종속성 정의, 조롱 및 단위 테스트 등)이 필요하지 않은 경우가 많습니다. 구조 맵을 참조 했으므로 구조 맵 기능 목록을보십시오 . 단순한 객체 매핑을 단순히 해결하는 것 이상입니다. 바퀴를 재발 명하지 마십시오!

당신이 가진 전부 망치라면 모든 것이 못처럼 보입니다

요구 사항 및 구축하는 응용 프로그램 유형에 따라 선택해야합니다. 프로젝트가 적거나 (1 ~ 2 개일 수 있음) 종속성이 거의없는 경우 더 간단한 접근 방식을 선택할 수 있습니다. 간단한 1 또는 2 개의 데이터베이스 호출에 Entity Framework를 사용하여 ADO .Net 데이터 액세스를 사용하는 것과 같습니다.이 시나리오에서 EF를 도입하는 것은 과도합니다.

그러나 더 큰 프로젝트 또는 프로젝트가 커지면 프레임 워크가있는 DI 레이어를 만들고 사용하는 DI 프레임 워크를 변경할 공간을 만드는 것이 좋습니다 (기본 앱의 외관 사용 (Web App, Web Api, Desktop) ..기타.).


1

팩토리를 사용하면 관련 인터페이스를 그룹화 할 수 있으므로 전달 된 매개 변수를 팩토리로 그룹화 할 수 있으면 constructor overinjection이 코드 를 볼 수 있는 좋은 솔루션입니다 *) :

public AddressModelFactory(IAddressAttributeService addressAttributeService,
        IAddressAttributeParser addressAttributeParser,
        ILocalizationService localizationService,
        IStateProvinceService stateProvinceService,
        IAddressAttributeFormatter addressAttributeFormatter)
    {
        this._addressAttributeService = addressAttributeService;
        this._addressAttributeParser = addressAttributeParser;
        this._localizationService = localizationService;
        this._stateProvinceService = stateProvinceService;
        this._addressAttributeFormatter = addressAttributeFormatter;
    }

생성자를 보시면 IAddressModelFactory거기 에 전달하면 되므로 매개 변수가 적습니다 *) :

 public CustomerController(IAddressModelFactory addressModelFactory,
        ICustomerModelFactory customerModelFactory,
        IAuthenticationService authenticationService,
        DateTimeSettings dateTimeSettings,
        TaxSettings taxSettings,
        ILocalizationService localizationService,
        IWorkContext workContext,
        IStoreContext storeContext,
        ICustomerService customerService,
        ICustomerAttributeParser customerAttributeParser,
        ICustomerAttributeService customerAttributeService,
        IGenericAttributeService genericAttributeService,
        ICustomerRegistrationService customerRegistrationService,
        ITaxService taxService,
        CustomerSettings customerSettings,
        AddressSettings addressSettings,...

CustomerController많은 매개 변수가 전달되는 것을 볼 수 있습니다. 그렇습니다 constructor overinjection. 이것은 DI가 작동하는 방식입니다. 그리고 아무것도 잘못되지 않았습니다 CustomerController.

*) 코드는 nopCommerce에서 가져온 것입니다.


1

간단한 용어로 Dependency Injection vs Factory 방법은 각각 푸시 대 풀 메커니즘을 의미합니다.

풀 메커니즘 : 클래스는 Factory Method에 간접적으로 의존하며, 구체적으로 구체적인 클래스에 의존합니다.

푸시 메커니즘 : 루트 구성 요소는 모든 종속 구성 요소를 단일 위치에 구성 할 수 있으므로 높은 유지 보수 및 느슨한 결합을 촉진합니다.

팩토리 메소드의 책임은 여전히 ​​의존성 주입과 같이 책임이 아웃소싱되는 추상화를 유출하는 비용으로 새로운 객체를 생성하기 위해 (간접적이지만) 클래스에 있습니다.


0

나는 이것들이 직교 적이며 함께 사용될 수 있다고 생각합니다. 최근에 직장에서 겪은 예를 보여 드리겠습니다.

우리는 DI를 위해 Java에서 Spring 프레임 워크를 사용하고있었습니다. 단일 클래스 ( Parent)는 다른 클래스 ( Child) 의 새 객체를 인스턴스화 해야하며 복잡한 공동 작업자가있었습니다.

@Component
class Parent {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
    }

    void method(int p) {
        Child c = new Child(dep1, dep2, ..., depN, p);
        // ...
    }
}

이 예제에서는 인스턴스를 생성자 에게 전달하기 위해서만 인스턴스 Parent를 수신 DepX해야합니다 Child. 이것에 관한 문제 :

  1. ParentChild그것보다 더 많은 지식을 가지고있다
  2. Parent 필요한 것보다 더 많은 공동 작업자가 있습니다
  3. Child변경 을 포함하는 종속성 추가Parent

이것은 내가 Factory여기에 완벽하게 맞는다는 것을 깨달았을 때 입니다.

  1. Child클래스 의 실제 매개 변수를 제외한 모든 매개 변수를 숨 깁니다.Parent
  2. ChildDI 구성에 집중할 수있는 의 생성 지식을 요약합니다 .

이것은 단순화 된 Parent클래스와 ChildFactory클래스입니다.

@Component
class Parent {
    // ...
    @Autowired
    Parent(ChildFactory childFactory) {
        this.childFactory = childFactory;
    }

    void method(int p) {
        Child c = childFactory.newChild(p);
        // ...
    }
}

@Component
class ChildFactory {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
        // ...
        this.depN = depN;
    }

    Child newChild(int p) {
        return new Child(dep1, dep2, ..., depN, p);
    }
}

0

이 시점에서 필요한 개체 유형을 정확히 알고있는 경우 종속성 주입을 사용합니다. 팩토리 패턴의 경우 필요한 정확한 객체 유형이 확실하지 않은 경우 객체 생성 프로세스를 팩토리에 위임하면됩니다.


-1

필자는이 두 가지모두 사용하여 관리 전략을 유지해야하는 개발자에게보다 가독성이 좋은 Inversion Of Control 전략을 만듭니다.

팩토리를 사용하여 다른 Layer 객체 (Business, Data Access)를 만듭니다.

ICarBusiness carBusiness = BusinessFactory.CreateCarBusiness();

다른 개발자는 이것을보고 Business Layer 개체를 만들 때 BusinessFactory에서보고 Intellisense는 개발자에게 만들 수있는 모든 가능한 비즈니스 계층을 제공합니다. 게임을 할 필요가 없습니다. 만들고 싶은 인터페이스를 찾으십시오.

이 구조는 이미 Inversion Of Control입니다. 더 이상 특정 개체를 만들 책임이 없습니다. 그러나 쉽게 변경하기 위해서는 Dependency Injection을 보장해야합니다. 나만의 커스텀 Dependency Injection을 만드는 것은 우스꽝 스럽기 때문에 Unity를 사용합니다. CreateCarBusiness () 내에서 Unity에 어떤 클래스가이 클래스에 속하는지 해결하도록 요청합니다.

내 코드 팩토리 종속성 주입 구조는 다음과 같습니다.

public static class BusinessFactory
{
    public static ICarBusiness CreateCarBusiness()
    {
       return Container.Resolve<ICarBusiness>();
    }
}

이제 두 가지 장점이 있습니다. 내 코드는 클래스를 만들 때 모든 객체를 사용할 수 있다고 말하는 Constructor Dependency Injection 대신 내가 사용하는 객체의 범위에 대해 다른 개발자가 더 읽기 쉽습니다.

단위 테스트를 만들 때 데이터베이스 데이터 액세스를 사용자 지정 코딩 된 데이터 액세스 계층으로 변경하는 데 사용합니다. 단위 테스트가 데이터베이스, 웹 서버, 전자 메일 서버 등과 통신하는 것을 원하지 않습니다. 인텔리전스가있는 곳이기 때문에 비즈니스 계층을 테스트해야합니다.


-4

다음과 같은 경우 종속성 삽입을 사용하는 것이 훨씬 좋습니다. 1. 하나의 큰 코드를 분리하는 데 잘 처리되므로 작은 파티션에 코드를 배포하십시오. 2. 테스트 가능성은 분리되지 않은 물체를 쉽게 조롱 할 수 있기 때문에 DI를 사용하는 것이 좋습니다. 인터페이스를 사용하면 각 객체를 쉽게 조롱하고 테스트 할 수 있습니다. 3. 느슨하게 분리 된 이후 다른 부분을 코딩 할 필요없이 프로그램의 각 부분을 동시에 수정할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.