Phụ thuộc tiêm so với mô hình nhà máy


498

Hầu hết các ví dụ được trích dẫn để sử dụng Dependency Injection, chúng ta cũng có thể giải quyết bằng cách sử dụng mẫu nhà máy. Có vẻ như khi nói đến việc sử dụng / thiết kế, sự khác biệt giữa tiêm phụ thuộc và nhà máy bị mờ hoặc mỏng.

Có lần ai đó nói với tôi rằng chính cách bạn sử dụng nó đã tạo nên sự khác biệt!

Tôi đã từng sử dụng StructMap một thùng chứa DI để giải quyết vấn đề, sau này tôi đã thiết kế lại nó để làm việc với một nhà máy đơn giản và loại bỏ các tham chiếu đến StructMap.

Bất cứ ai có thể cho tôi biết sự khác biệt giữa chúng và nơi để sử dụng những gì, thực hành tốt nhất ở đây là gì?


21
Hai cách tiếp cận này có thể khen nhau không: sử dụng Dependency Injection để tiêm các lớp của nhà máy?
Richard Ev

20
Sẽ thực sự tốt đẹp nếu câu hỏi này có câu trả lời với một số mã trong đó! Tôi vẫn không thấy DI sẽ có lợi / khác với việc sử dụng một nhà máy để sáng tạo như thế nào? Bạn sẽ chỉ cần thay thế một dòng trong lớp nhà máy để thay đổi obj / triển khai được tạo?
gideon

2
@gideon sẽ không buộc bạn biên dịch ứng dụng của bạn, hoặc ít nhất là mô-đun có chứa lớp nhà máy?
axit lysergic

1
@liortal yep đúng vậy. Đã có một nghiên cứu dài về DI kể từ nhận xét đó và bây giờ tôi hiểu rằng DI đưa phương pháp nhà máy đi trước một bước.
gideon

1
Kiểm tra câu trả lời tuyệt vời này: stackoverflow.com/questions/4985455/ từ - anh ấy nói nó rất hay và cung cấp các mẫu mã.
Luis Perez

Câu trả lời:


293

Khi sử dụng một nhà máy, mã của bạn vẫn thực sự chịu trách nhiệm tạo các đối tượng. Bởi DI bạn thuê ngoài trách nhiệm đó cho một lớp khác hoặc một khung, tách biệt với mã của bạn.


172
Mẫu DI không yêu cầu bất kỳ khuôn khổ nào. Bạn có thể làm DI bằng cách viết thủ công các nhà máy làm DI. Khung DI chỉ làm cho nó dễ dàng hơn.
Esko Luontola

28
@Perpetualcoder - Cảm ơn @Esko - Đừng để bị cuốn vào khung từ có nghĩa là một thư viện bên thứ 3 tiên tiến.
willcodejavaforfood

4
+1 @willcode cảm ơn! Vì vậy, bạn phải thay đổi tập tin config / xml thay thế. Tôi hiểu rồi. Liên kết wiki en.wikipedia.org/wiki/ đá định nghĩa mẫu nhà máy làManually-Injected Dependency
gideon

5
Tôi không có lợi thế khi thay đổi 1 dòng XML so với thay đổi 1 dòng mã. Bạn có thể giải thích?
Rafael Eyng 19/03/2015

1
Lặp lại câu trả lời OP thay thế "DI" bằng "nhà máy", vẫn có ý nghĩa.
Edson Medina

219

Tôi sẽ đề nghị để giữ cho các khái niệm đơn giản và đơn giản. Dependency Injection là một mô hình kiến ​​trúc cho các thành phần phần mềm ghép lỏng lẻo. Mô hình nhà máy chỉ là một cách để phân tách trách nhiệm tạo đối tượng của các lớp khác với thực thể khác. Mô hình nhà máy có thể được gọi là một công cụ để thực hiện DI. Việc tiêm phụ thuộc có thể được thực hiện theo nhiều cách như DI bằng cách sử dụng các hàm tạo, sử dụng ánh xạ các tệp xml, v.v.


4
Đó là sự thật rằng Mô hình nhà máy là một trong những cách thực hiện Dependency Injection. Một lợi ích khác khi sử dụng Dependency Injection đối với Mô hình nhà máy là DI Framework sẽ cung cấp cho bạn sự linh hoạt về cách đăng ký trừu tượng với các loại cụ thể của bạn. Chẳng hạn như Mã như Cấu hình, XML hoặc Cấu hình tự động. Tính linh hoạt đó sẽ cung cấp cho bạn quản lý trọn đời các đối tượng của bạn hoặc quyết định cách thức và thời điểm đăng ký giao diện của bạn.
Teoman shipahi

1
Mô hình nhà máy cung cấp sự trừu tượng hóa ở mức cao hơn DI. Một nhà máy có thể cung cấp các tùy chọn cấu hình giống như DI, ​​nhưng nó cũng có thể chọn ẩn tất cả các chi tiết cấu hình đó, để nhà máy có thể quyết định chuyển sang triển khai hoàn toàn khác mà không cần khách hàng biết. Bản thân DI không cho phép điều này. DI yêu cầu khách hàng chỉ định những thay đổi mà anh ta muốn.
nơ ron

1
Câu trả lời tốt đẹp. Nhiều người đang bối rối với câu hỏi đó: "Tôi nên chọn loại pattens nào?" Trong thực tế, các mẫu thiết kế chỉ là một cách để giải quyết vấn đề ở các tình huống thích hợp. Nếu bạn không biết bạn nên sử dụng mẫu thiết kế nào hoặc "Tôi nên triển khai mẫu ở đâu?" sau đó bạn không cần điều đó vào lúc này.
Benyi

2
Tôi nghĩ chính xác hơn khi nói Factory Pattern là một công cụ để triển khai IoC (không phải DI). Xin lưu ý rằng DI là một dạng của IoC
Hooman Bahreini

185

Phụ thuộc tiêm

Thay vì khởi tạo các bộ phận chính nó, một chiếc xe yêu cầu các bộ phận cần thiết để hoạt động.

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;
    }
}

Nhà máy

Đặt các mảnh lại với nhau để tạo thành một đối tượng hoàn chỉnh và ẩn loại bê tông khỏi người gọi.

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;
    }   
}

Kết quả

Như bạn có thể thấy, các nhà máy và DI bổ sung cho nhau.

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

Bạn có nhớ goldilocks và ba con gấu không? Vâng, tiêm phụ thuộc là loại như thế. Dưới đây là ba cách để làm điều tương tự.

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();
}

Ví dụ # 1 - Đây là điều tồi tệ nhất vì nó hoàn toàn che giấu sự phụ thuộc. Nếu bạn xem phương pháp này là một hộp đen, bạn sẽ không biết nó cần một chiếc xe hơi.

Ví dụ # 2 - Điều này tốt hơn một chút vì bây giờ chúng tôi biết rằng chúng tôi cần một chiếc xe hơi kể từ khi chúng tôi đi qua trong một nhà máy xe hơi. Nhưng lần này chúng ta đang vượt qua quá nhiều vì tất cả các phương pháp thực sự cần là một chiếc xe hơi. Chúng tôi đang đi qua một nhà máy chỉ để chế tạo xe khi chiếc xe có thể được chế tạo ngoài phương pháp và được chuyển vào.

Ví dụ # 3 - Điều này là lý tưởng vì phương thức yêu cầu chính xác những gì nó cần. Không quá nhiều hoặc quá ít. Tôi không phải viết MockCarFactory chỉ để tạo MockCars, tôi có thể chuyển thẳng bản giả. Nó trực tiếp và giao diện không nói dối.

Bài nói chuyện trên Google Tech của Misko Hevery thật tuyệt vời và là nền tảng của những gì tôi lấy ví dụ từ đó. http://www.youtube.com/watch?v=XcT4yYu_TTs


1
Mô hình nhà máy được tiêm là DI.
Mangesh Pimpalkar

7
@PhilGoetz Những gì bạn đang mô tả nghe giống như mẫu Trình định vị dịch vụ. Họ có những mục tiêu tương tự, trong đó họ nhằm mục đích tách rời các dịch vụ từ người tiêu dùng của họ. Tuy nhiên, có nhiều nhược điểm đối với mẫu Định vị dịch vụ. Chủ yếu, che giấu sự phụ thuộc không phải là một điều tốt. Người tiêu dùng vẫn cần có được sự phụ thuộc của nó, nhưng thay vì xác định một giao diện rõ ràng, họ được thông qua một cách 'bí mật' và dựa vào trạng thái toàn cầu. Trong ví dụ, người tiêu dùng không có ý tưởng gì về nhà máy, họ chỉ hỏi về những gì họ cần và không phải quan tâm đến việc nhu cầu đó được xây dựng như thế nào.
Despertar

1
@MatthewWhited Hầu hết các quyết định thiết kế đều có sự đánh đổi. Với DI, bạn có được sự linh hoạt, minh bạch và một hệ thống dễ kiểm tra hơn, nhưng với chi phí tiết lộ ruột của bạn. Nhiều người thấy đây là một sự đánh đổi chấp nhận được. Điều này không có nghĩa là DI luôn là giải pháp tốt nhất và câu hỏi này không phải là về điều đó. Ví dụ này có nghĩa là cho thấy sự khác biệt giữa DI và mẫu nhà máy bằng cách hiển thị một số cách sử dụng tốt và xấu của từng mẫu.
Despertar

3
Đây không phải là mẫu DI, đây chỉ là một triển khai của IoC. DI sử dụng ServiceLocator để xác định sự phụ thuộc đúng và đưa nó vào hàm tạo trong quá trình tạo đối tượng, mã của bạn chỉ tách riêng việc tạo các đối tượng khỏi phạm vi lớp của bạn.
Diego Mendes

3
@DiegoMendes Những gì bạn đang mô tả là một khung tự động hóa DI. ServiceLocator khi bạn gọi nó là DI cho bạn. Những gì tôi đang thể hiện là bản thân mẫu không yêu cầu bất kỳ khung ưa thích nào. Xem stackoverflow.com/a/140655/1160036 hoặc xem en.wikipedia.org/wiki/iêu : "[danh sách khung] hỗ trợ tiêm phụ thuộc nhưng không bắt buộc phải tiêm phụ thuộc". Ngoài ra, "Tiêm là việc truyền phụ thuộc vào đối tượng phụ thuộc". Bạn đang làm phức tạp một khái niệm đẹp đơn giản.
Despertar

50

Lý do Dependency Injection (DI) và Factory Forms giống nhau là vì chúng là hai triển khai Inversion of Control (IoC) là một kiến ​​trúc phần mềm. Nói một cách đơn giản, chúng là hai giải pháp cho cùng một vấn đề.

Vì vậy, để trả lời câu hỏi, sự khác biệt chính giữa mẫu Factory và DI là cách thu được tham chiếu đối tượng. Với nội xạ phụ thuộc như tên ngụ ý tham chiếu được tiêm hoặc đưa vào mã của bạn. Với mẫu Factory, mã của bạn phải yêu cầu tham chiếu để mã của bạn tìm nạp đối tượng. Cả hai triển khai đều loại bỏ hoặc tách rời liên kết giữa mã và lớp hoặc loại cơ sở của tham chiếu đối tượng đang được sử dụng bởi mã.

Điều đáng chú ý là các mẫu Factory (hoặc thực sự là các mẫu Tóm tắt của Factory là các nhà máy trả về các nhà máy mới trả về các tham chiếu đối tượng) có thể được viết để chọn hoặc liên kết động với loại hoặc lớp đối tượng được yêu cầu trong thời gian chạy. Điều này làm cho chúng rất giống (thậm chí nhiều hơn DI) với mẫu Trình định vị dịch vụ, đây là một triển khai khác của IoC.

Mẫu thiết kế Factory khá cũ (về phần mềm) và đã xuất hiện được một thời gian. Kể từ sự phổ biến gần đây của mô hình kiến ​​trúc IoC, nó đang có sự hồi sinh.

Tôi đoán khi nói đến các mẫu thiết kế IoC: kim phun được tiêm, định vị được định vị và các nhà máy đã được tái cấu trúc.


3
Đây là câu trả lời tốt nhất ... các câu trả lời khác không đề cập đến IoC hoặc không nhận ra rằng DI là một dạng của IoC.
Hooman Bahreini

Cảm ơn, khi tôi học IOC lần đầu tiên, tôi gần như hét lên rằng đây chỉ là một dạng khác của mô hình nhà máy, hóa ra chúng thực sự giống nhau
Harvey Lin

40

Có những vấn đề dễ giải quyết với việc tiêm phụ thuộc mà không dễ giải quyết bằng một bộ các nhà máy.

Một số khác biệt giữa, một mặt, đảo ngược kiểm soát và tiêm phụ thuộc (IOC / DI), và mặt khác, một công cụ định vị dịch vụ hoặc một bộ các nhà máy (nhà máy), là:

IOC / DI là một hệ sinh thái hoàn chỉnh của các đối tượng và dịch vụ miền trong chính nó. Nó thiết lập mọi thứ cho bạn theo cách bạn chỉ định. Các đối tượng và dịch vụ miền của bạn được xây dựng bởi container và không tự xây dựng: do đó chúng không có bất kỳ sự phụ thuộc nào vào container hoặc trên bất kỳ nhà máy nào. IOC / DI cho phép mức cấu hình cực kỳ cao, với tất cả cấu hình ở một nơi duy nhất (xây dựng bộ chứa) ở lớp trên cùng của ứng dụng của bạn (GUI, giao diện người dùng Web).

Nhà máy trừu tượng hóa một số việc xây dựng các đối tượng và dịch vụ tên miền của bạn. Nhưng các đối tượng và dịch vụ miền vẫn chịu trách nhiệm tìm ra cách tự xây dựng và làm thế nào để có được tất cả những thứ mà chúng phụ thuộc. Tất cả các phụ thuộc "hoạt động" này lọc tất cả các lớp trong ứng dụng của bạn. Không có nơi duy nhất để đi để cấu hình tất cả mọi thứ.


26

Một nhược điểm của DI là nó không thể khởi tạo các đối tượng bằng logic. Ví dụ, khi tôi cần tạo một nhân vật có tên và tuổi ngẫu nhiên, DI không phải là lựa chọn trên mẫu nhà máy. Với các nhà máy, chúng ta có thể dễ dàng gói gọn thuật toán ngẫu nhiên từ việc tạo đối tượng, hỗ trợ một trong các mẫu thiết kế có tên là "Đóng gói những gì thay đổi".


22

Quản lý vòng đời là một trong những thùng chứa phụ thuộc trách nhiệm ngoài việc khởi tạo và tiêm. Thực tế là container đôi khi giữ một tham chiếu đến các thành phần sau khi khởi tạo là lý do nó được gọi là "container" chứ không phải là một nhà máy. Các thùng chứa thuốc tiêm phụ thuộc thường chỉ giữ một tham chiếu đến các đối tượng cần thiết để quản lý vòng đời hoặc được sử dụng lại cho các mũi tiêm trong tương lai, như singletons hoặc flyweights. Khi được cấu hình để tạo phiên bản mới của một số thành phần cho mỗi cuộc gọi đến vùng chứa, vùng chứa thường chỉ quên về đối tượng được tạo.

Từ: http://tutorials.jenkov.com/dependency-injection/dependency-injection-containers.html


15

Tôi tin rằng DI là một loại lớp trừu tượng trong các nhà máy, nhưng chúng cũng cung cấp lợi ích ngoài sự trừu tượng hóa. Một nhà máy thực sự biết làm thế nào để khởi tạo một loại duy nhất và cấu hình nó. Một lớp DI tốt cung cấp khả năng, thông qua cấu hình, để khởi tạo và cấu hình nhiều loại.

Rõ ràng, đối với một dự án có một vài loại đơn giản đòi hỏi logic kinh doanh tương đối ổn định trong xây dựng của họ, mô hình nhà máy rất đơn giản để hiểu, thực hiện và hoạt động tốt.

OTOH, nếu bạn có một dự án chứa nhiều loại mà việc triển khai bạn muốn thay đổi thường xuyên, DI cho phép bạn linh hoạt thông qua cấu hình của nó để thực hiện việc này trong thời gian chạy mà không phải biên dịch lại các nhà máy của bạn.


14

Học thuyết

Có hai điểm quan trọng cần xem xét:

  1. Ai tạo ra đồ vật

    • [Factory]: Bạn phải viết đối tượng CÁCH nên được tạo. Bạn có lớp Factory riêng biệt chứa logic tạo.
    • [Dependency Injection]: Trong các trường hợp thực tế được thực hiện bởi các khung bên ngoài (ví dụ trong Java sẽ là spring / ejb / guice). Tiêm xảy ra "một cách kỳ diệu" mà không cần tạo ra các vật thể mới
  2. Những loại đối tượng mà nó quản lý:

    • [Nhà máy]: Thường chịu trách nhiệm tạo ra các đối tượng có trạng thái
    • [Tiêm phụ thuộc] Nhiều khả năng tạo các đối tượng không trạng thái

Ví dụ thực tế về cách sử dụng cả nhà máy và tiêm phụ thuộc trong một dự án

  1. Những gì chúng tôi muốn xây dựng

Mô-đun ứng dụng để tạo đơn hàng chứa nhiều mục được gọi là thứ tự.

  1. Ngành kiến ​​trúc

Giả sử chúng ta muốn tạo kiến ​​trúc lớp sau:

nhập mô tả hình ảnh ở đây

Các đối tượng miền có thể là các đối tượng được lưu trữ bên trong cơ sở dữ liệu. Kho lưu trữ (DAO) giúp thu hồi các đối tượng từ cơ sở dữ liệu. Dịch vụ cung cấp API cho các mô-đun khác. Cung cấp cho các hoạt động trên ordermô-đun

  1. Lớp tên miền và cách sử dụng của các nhà máy

Các thực thể sẽ có trong cơ sở dữ liệu là Order và OrderLine. Đơn hàng có thể có nhiều OrderLines. Mối quan hệ giữa Order và OrderLine

Bây giờ đến phần thiết kế quan trọng. Các mô-đun bên ngoài cái này có nên tự tạo và quản lý OrderLines không? Không. Dòng đơn hàng chỉ tồn tại khi bạn có Đơn hàng được liên kết với nó. Sẽ là tốt nhất nếu bạn có thể ẩn triển khai nội bộ với các lớp bên ngoài.

Nhưng làm thế nào để tạo Order mà không có kiến ​​thức về OrderLines?

Nhà máy

Ai đó muốn tạo đơn hàng mới đã sử dụng OrderFactory (sẽ ẩn chi tiết về thực tế cách chúng tôi tạo Đơn hàng).

nhập mô tả hình ảnh ở đây

Đó là cách nó sẽ nhìn vào bên trong IDE. Các lớp bên ngoài domaingói sẽ sử dụng OrderFactorythay cho hàm tạo bên trongOrder

  1. Tiêm phụ thuộc Tiêm phụ thuộc thường được sử dụng nhiều hơn với các lớp không trạng thái như kho lưu trữ và dịch vụ.

OrderRep repository và OrderService được quản lý theo khung tiêm phụ thuộc. Kho lưu trữ chịu trách nhiệm quản lý các hoạt động CRUD trên cơ sở dữ liệu. Dịch vụ tiêm Kho lưu trữ và sử dụng nó để lưu / tìm các lớp miền chính xác.

nhập mô tả hình ảnh ở đây


Bạn có thể vui lòng làm rõ điều này? [Factory]: Usually responsible for creation of stateful objects [Dependency Injections] More likely to create stateless objectsTôi không hiểu tại sao
Maksim Dmitriev

2
Các đối tượng trạng thái có khả năng nằm trong lớp ứng dụng miền nơi bạn ánh xạ dữ liệu của mình vào cơ sở dữ liệu mà bạn thường không sử dụng phép nội xạ phụ thuộc ở đó. Nhà máy được sử dụng thường xuyên để tạo AggregateRoot từ cây của các vật thể phức tạp
Marcin Szymczak

12

Tôi biết câu hỏi này đã cũ nhưng tôi muốn thêm năm xu của mình,

Tôi nghĩ rằng tiêm phụ thuộc (DI) theo nhiều cách giống như Mô hình nhà máy (FP) có thể định cấu hình và theo nghĩa đó, bất cứ điều gì bạn có thể làm với DI, bạn sẽ có thể thực hiện với nhà máy đó.

Trên thực tế, nếu bạn sử dụng spring chẳng hạn, bạn có tùy chọn tự động cấp tài nguyên (DI) hoặc làm một cái gì đó như thế này:

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

Và sau đó sử dụng ví dụ 'mb' đó để làm bất cứ điều gì. Không phải đó là một cuộc gọi đến một nhà máy sẽ trả lại cho bạn một ví dụ sao ??

Sự khác biệt thực sự duy nhất tôi nhận thấy giữa hầu hết các ví dụ về FP là bạn có thể định cấu hình "myBean" trong xml hoặc trong một lớp khác và một khung công tác sẽ hoạt động như một nhà máy, nhưng ngoài điều đó là điều tương tự, và bạn có thể có một Nhà máy chắc chắn đọc tệp cấu hình hoặc thực hiện khi cần.

Và nếu bạn hỏi ý kiến ​​của tôi (Và tôi biết bạn đã không), tôi tin rằng DI làm điều tương tự nhưng chỉ làm tăng thêm sự phức tạp cho sự phát triển, tại sao?

tốt, vì một điều, để bạn biết cách triển khai đang được sử dụng cho bất kỳ bean nào bạn tự động với DI, bạn phải tự đi đến cấu hình.

nhưng ... những gì về lời hứa mà bạn sẽ không phải biết về việc thực hiện đối tượng bạn đang sử dụng thì sao? pfft! nghiêm túc? khi bạn sử dụng một cách tiếp cận như thế này ... bạn có giống như vậy không khi viết bài thực hiện ?? và ngay cả khi bạn không, bạn gần như không thể nhìn vào cách thức thực hiện những gì nó phải làm ??

và đối với một điều cuối cùng, không có vấn đề bao nhiêu khung DI hứa với bạn rằng bạn sẽ xây dựng mọi thứ tách rời khỏi nó, không phụ thuộc vào các lớp của chúng, nếu bạn đang sử dụng một khung công tác bạn xây dựng mọi thứ, nếu bạn phải thay đổi cách tiếp cận hoặc khuôn khổ nó sẽ không phải là một nhiệm vụ dễ dàng ... EVER! ... Nhưng, vì bạn buil tất cả mọi thứ xung quanh khuôn khổ cụ thể đó thay vì lo lắng về giải pháp tốt nhất cho doanh nghiệp của bạn, thì bạn sẽ phải đối mặt với một vấn đề sinh học khi làm điều đó.

Trên thực tế, ứng dụng kinh doanh thực sự duy nhất cho cách tiếp cận FP hoặc DI mà tôi có thể thấy là nếu bạn cần thay đổi các triển khai đang được sử dụng trong thời gian chạy , nhưng ít nhất các khung tôi biết không cho phép bạn làm điều đó, bạn phải rời khỏi mọi thứ hoàn hảo trong cấu hình tại thời điểm phát triển nếu bạn cần sử dụng phương pháp khác.

Vì vậy, nếu tôi có một lớp thực hiện khác nhau ở hai phạm vi trong cùng một ứng dụng (giả sử, hai công ty đang nắm giữ) tôi phải cấu hình khung để tạo hai loại đậu khác nhau và điều chỉnh mã của tôi để sử dụng từng loại. Không giống như tôi sẽ viết một cái gì đó như thế này:

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

giống như thế này:

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

Và điều này:

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

Trong mọi trường hợp, bạn sẽ phải thay đổi một cái gì đó trong ứng dụng của mình, cho dù các lớp hoặc tệp cấu hình, nhưng bạn sẽ phải thực hiện lại nó.

Sẽ không tốt nếu làm điều gì đó như thế này:

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

Và theo cách đó, bạn đặt mã của nhà máy để có được triển khai đúng trong thời gian chạy tùy thuộc vào doanh nghiệp người dùng đã đăng nhập ?? Bây giờ THAT sẽ hữu ích. Bạn chỉ có thể thêm một jar mới với các lớp mới và đặt quy tắc thậm chí có thể trong thời gian chạy (hoặc thêm tệp cấu hình mới nếu bạn để tùy chọn này mở), không thay đổi đối với các lớp hiện có. Đây sẽ là một nhà máy năng động!

Sẽ không hữu ích hơn việc phải viết hai cấu hình cho mỗi doanh nghiệp và thậm chí có thể có hai ứng dụng khác nhau cho mỗi doanh nghiệp ??

Bạn có thể nói với tôi, tôi không cần thực hiện chuyển đổi trong thời gian chạy, vì vậy tôi định cấu hình ứng dụng và nếu tôi kế thừa lớp hoặc sử dụng một triển khai khác, tôi chỉ cần thay đổi cấu hình và triển khai lại. Ok, điều đó cũng có thể được thực hiện với một nhà máy. Và thành thật mà nói, bạn làm điều này bao nhiêu lần? có lẽ chỉ khi bạn có một ứng dụng sẽ được sử dụng ở một nơi khác trong công ty của bạn và bạn sẽ chuyển mã cho một nhóm khác và họ sẽ làm những việc như thế này. Nhưng này, điều đó cũng có thể được thực hiện với nhà máy, và sẽ còn tuyệt vời hơn với một nhà máy năng động !!

Dù sao, phần bình luận nếu mở cho bạn để giết tôi.


3
Quá nhiều nhà phát triển nghĩ rằng các khung Dependency Injection được tạo ra để mang lại một cái gì đó mới. Như bạn giải thích rõ, các nhà máy truyền thống (thường là nhà máy trừu tượng) có thể đóng vai trò tương tự đảo ngược phụ thuộc. Nghịch đảo vs tiêm? Đối với tôi, "lợi ích" duy nhất của khung Dependency Injection là chúng tôi không thay đổi / biên dịch lại mã (vì thường được cấu hình với XML như Spring) khi phụ thuộc được thêm / thay đổi. Tại sao phải tránh biên dịch lại? Để tránh một số lỗi tiềm ẩn của con người trong khi thay đổi phụ thuộc / nhà máy. Nhưng với các IDE tuyệt vời của chúng tôi, tái cấu trúc chơi tốt :)
Mik378

6

IOC là một khái niệm được thực hiện theo hai cách. Tạo phụ thuộc và tiêm phụ thuộc, Nhà máy / Nhà máy trừu tượng là ví dụ về tạo phụ thuộc. Phụ thuộc tiêm là constructor, setter và giao diện. Cốt lõi của IOC là không phụ thuộc vào các lớp cụ thể, mà xác định trừu tượng của các phương thức (nói một lớp Giao diện / trừu tượng) và sử dụng trừu tượng đó để gọi phương thức của lớp cụ thể. Giống như mẫu Factory trả về lớp cơ sở hoặc giao diện. Phép tiêm phụ thuộc Sameiliy sử dụng lớp / giao diện cơ sở để đặt giá trị cho các đối tượng.


4

Với việc tiêm phụ thuộc, khách hàng không cần phải tự phụ thuộc, tất cả đã được chuẩn bị trước.

Với các nhà máy, ai đó phải gọi những người đó để đưa các vật thể được tạo ra đến nơi cần thiết.

Sự khác biệt chủ yếu nằm ở một dòng này khi gọi nhà máy và tìm nạp đối tượng được xây dựng.

Nhưng với các nhà máy, bạn phải viết 1 dòng này ở mọi nơi bạn cần một đối tượng như vậy. Với DI, bạn chỉ cần tạo hệ thống dây điện (mối quan hệ giữa việc sử dụng và đối tượng được tạo) một lần và chỉ dựa vào sự hiện diện của đối tượng sau đó ở mọi nơi. Mặt khác, DI thường đòi hỏi nhiều hơn một chút (bao nhiêu tùy thuộc vào khung) làm việc ở phía chuẩn bị.


3

Tôi đã có cùng một câu hỏi ngay khi tôi đọc về DI và kết thúc tại bài viết này. Vì vậy, cuối cùng đây là những gì tôi hiểu nhưng xin vui lòng sửa chữa cho tôi nếu tôi sai.

"Từ lâu đã có những vương quốc nhỏ với các cơ quan quản lý riêng của họ kiểm soát và đưa ra quyết định dựa trên các quy tắc bằng văn bản của chính họ. Sau đó, thành lập một chính phủ lớn loại bỏ tất cả các cơ quan quản lý nhỏ này có một bộ quy tắc (hiến pháp) và được thực thi thông qua các tòa án"

Các cơ quan quản lý của vương quốc nhỏ là "Các nhà máy"

Chính phủ lớn là "Phụ thuộc vào".


1
Vì vậy, KHI NÀO một người lập trình một chính phủ nhỏ trước đây bây giờ sẽ lớn? Bằng biện pháp nào?
Tù nhân ZERO

3

Bạn có thể xem liên kết này để so sánh hai cách tiếp cận (và các phương pháp khác) trong một ví dụ thực tế.

Về cơ bản, khi các yêu cầu thay đổi, cuối cùng bạn sẽ sửa đổi nhiều mã hơn nếu bạn sử dụng các nhà máy thay vì DI.

Điều này cũng hợp lệ với DI thủ công (nghĩa là khi không có khung bên ngoài cung cấp các phụ thuộc cho các đối tượng của bạn, nhưng bạn chuyển chúng trong mỗi hàm tạo).


3

Hầu hết các câu trả lời ở đây giải thích sự khác biệt về khái niệm và chi tiết thực hiện của cả hai. Tuy nhiên, tôi không thể tìm thấy lời giải thích về sự khác biệt trong ứng dụng mà IMO là quan trọng nhất và OP đã hỏi về. Vì vậy, hãy để tôi mở lại chủ đề này ...

Có lần ai đó nói với tôi rằng chính cách bạn sử dụng nó đã tạo nên sự khác biệt!

Chính xác. Trong 90% trường hợp, bạn có thể có được tham chiếu đối tượng bằng cách sử dụng Factory hoặc DI và thông thường bạn kết thúc với cái sau. Trong 10% trường hợp khác sử dụng Factory chỉcách chính xác . Những trường hợp này bao gồm lấy các đối tượng theo biến tại các tham số thời gian chạy. Như thế này:

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

Trong trường hợp này, việc nhận được clienttừ DI là không thể (hoặc ít nhất là yêu cầu một cách giải quyết xấu xí). Vì vậy, như một quy tắc chung để đưa ra quyết định: nếu có thể có được một phụ thuộc mà không có bất kỳ tham số tính toán thời gian chạy nào thì DI được ưu tiên, nếu không thì sử dụng Factory.


2

Tôi tin rằng DI là một cách để cấu hình hoặc khởi tạo một bean. DI có thể được thực hiện theo nhiều cách như constructor, setter-getter, v.v.

Mô hình nhà máy chỉ là một cách khác để bắt đầu đậu. mẫu này sẽ được sử dụng chủ yếu khi bạn phải tạo các đối tượng bằng mẫu thiết kế gốc, bởi vì trong khi sử dụng mẫu này, bạn không cấu hình các thuộc tính của một bean, chỉ khởi tạo đối tượng.

Kiểm tra liên kết này: Dependency Injection


2

Binoj,

Tôi không nghĩ bạn phải chọn cái khác.

Hành động di chuyển một lớp phụ thuộc hoặc giao diện sang một hàm tạo hoặc trình thiết lập lớp tuân theo mẫu DI. Đối tượng bạn chuyển đến hàm tạo hoặc bộ có thể được thực hiện với Factory.

Khi nào sử dụng? Sử dụng các mẫu hoặc các mẫu trong nhà xe của nhà phát triển của bạn. Những gì họ cảm thấy thoải mái nhất và tìm thấy dễ hiểu nhất.


2

Tôi tin rằng, 3 khía cạnh quan trọng chi phối các đối tượng và cách sử dụng của chúng:
1. Khởi tạo (của một lớp cùng với khởi tạo nếu có).
2. Tiêm (ví dụ được tạo ra) khi cần thiết.
3. Quản lý vòng đời (ví dụ được tạo ra).

Sử dụng mẫu Factory, khía cạnh đầu tiên (khởi tạo) đạt được nhưng hai khía cạnh còn lại là nghi vấn. Lớp sử dụng các thể hiện khác phải mã hóa cứng các nhà máy (thay vì các thể hiện được tạo ra) gây cản trở khả năng khớp nối lỏng lẻo. Hơn thế nữa, quản lý vòng đờicác trường hợp trở thành một thách thức trong một ứng dụng lớn trong đó một nhà máy được sử dụng ở nhiều nơi (đặc biệt, nếu nhà máy không quản lý vòng đời của cá thể mà nó trả về, nó sẽ trở nên xấu xí).

Mặt khác, sử dụng DI (của mẫu IoC), cả 3 được trừu tượng hóa bên ngoài mã (tới bộ chứa DI) và bean được quản lý không cần gì về sự phức tạp này. Loose Coupling , một mục tiêu kiến ​​trúc rất quan trọng có thể đạt được yên tĩnh thoải mái. Một mục tiêu kiến ​​trúc quan trọng khác, việc phân tách các mối quan tâm có thể đạt được tốt hơn nhiều so với các nhà máy.

Trong khi các nhà máy có thể phù hợp cho các ứng dụng nhỏ, thì các nhà máy lớn sẽ tốt hơn khi chọn DI trên các nhà máy.


1

Suy nghĩ của tôi:

Dependword Injection: truyền cộng tác viên làm tham số cho các nhà xây dựng. Dependency Injection Framework: một nhà máy chung và có thể cấu hình để tạo các đối tượng để truyền dưới dạng tham số cho các hàm tạo.


1
Đúng chính xác. Hầu như tất cả các đề cập về Dependency Injection (DI) trong Hỏi & Đáp này sử dụng sai thuật ngữ trái ngược với định nghĩa này. Container tiêm phụ thuộc (DIC) là khung phổ biến nhất hoạt động như một nhà máy chung và có thể định cấu hình để tạo các đối tượng.
CXJ

1

Khung tiêm là một triển khai của Mô hình nhà máy.

Tất cả phụ thuộc vào yêu cầu của bạn. Nếu bạn có nhu cầu triển khai mẫu nhà máy trong một ứng dụng, rất có thể các yêu cầu của bạn sẽ được đáp ứng bởi một trong vô số các triển khai khung tiêm ngoài kia.

Bạn chỉ nên đưa ra giải pháp của riêng mình nếu các yêu cầu của bạn không thể được đáp ứng bởi bất kỳ khuôn khổ bên thứ 3 nào. Bạn viết càng nhiều mã, bạn càng phải duy trì mã. Mã là một trách nhiệm không phải là một tài sản.

Các lập luận về việc triển khai bạn nên sử dụng không quan trọng về cơ bản như hiểu được nhu cầu kiến ​​trúc của ứng dụng của bạn.


1

Mẫu thiết kế nhà xưởng

Các mẫu thiết kế nhà máy được đặc trưng bởi

  • Giao diện
  • Lớp thực hiện
  • Nhà máy

Bạn có thể quan sát vài điều khi bạn tự đặt câu hỏi như dưới đây

  • Khi nào nhà máy sẽ tạo đối tượng cho các lớp thực hiện - thời gian chạy hoặc biên dịch thời gian?
  • Điều gì nếu bạn muốn chuyển đổi việc thực hiện trong thời gian chạy? - Không thể

Chúng được xử lý bằng cách tiêm Dependency.

Phụ thuộc tiêm

Bạn có thể có những cách khác nhau để bạn có thể tiêm phụ thuộc. Để đơn giản, hãy đi với Giao diện tiêm

Trong DI, container tạo các thể hiện cần thiết và "tiêm" chúng vào đối tượng.

Do đó loại bỏ việc khởi tạo tĩnh.

Thí dụ:

public class MyClass{

  MyInterface find= null;

  //Constructor- During the object instantiation

  public MyClass(MyInterface myInterface ) {

       find = myInterface ;
  }

  public void myMethod(){

       find.doSomething();

  }
}

1

Từ một mệnh giá trông chúng giống nhau

Nói một cách rất đơn giản, Mô hình nhà máy, Mô hình sáng tạo giúp tạo cho chúng ta một đối tượng - "Xác định giao diện để tạo đối tượng". Nếu chúng ta có một loại giá trị khóa của nhóm đối tượng (ví dụ: Từ điển), chuyển khóa cho Nhà máy (tôi đang đề cập đến Mẫu nhà máy đơn giản), bạn có thể giải quyết Loại. Công việc hoàn thành! Mặt khác, Khung tiêm phụ thuộc (như Bản đồ cấu trúc, Ninject, Unity ... vv) dường như cũng đang làm điều tương tự.

Nhưng ... "Đừng phát minh lại bánh xe"

Từ góc độ kiến ​​trúc, nó là một lớp ràng buộc và "Đừng phát minh lại bánh xe".

Đối với một ứng dụng cấp doanh nghiệp, khái niệm DI là một lớp kiến ​​trúc xác định các phụ thuộc. Để đơn giản hóa điều này hơn nữa, bạn có thể nghĩ về điều này như một dự án thư viện lớp riêng biệt, giải quyết sự phụ thuộc. Ứng dụng chính phụ thuộc vào dự án này trong đó trình giải quyết phụ thuộc đề cập đến các triển khai cụ thể khác và giải quyết phụ thuộc.

Việc sử dụng "GetType / Tạo" từ một Nhà máy, thường xuyên hơn là chúng ta không cần nhiều tính năng hơn (khả năng sử dụng XML để xác định các phụ thuộc, chế độ thử nghiệm và kiểm tra đơn vị, v.v.). Vì bạn đã tham khảo Bản đồ cấu trúc, hãy xem danh sách tính năng Bản đồ cấu trúc . Rõ ràng không chỉ đơn giản là giải quyết Ánh xạ đối tượng đơn giản. Đừng phát minh lại bánh xe!

Nếu tất cả những gì bạn có là một cái búa, mọi thứ trông giống như một cái đinh

Tùy thuộc vào yêu cầu của bạn và loại ứng dụng bạn xây dựng, bạn cần đưa ra lựa chọn. Nếu nó chỉ có một vài dự án (có thể là một hoặc hai ..) và liên quan đến một vài phụ thuộc, bạn có thể chọn một cách tiếp cận đơn giản hơn. Giống như sử dụng ADO .Net truy cập dữ liệu bằng cách sử dụng Entity Framework cho các cuộc gọi cơ sở dữ liệu 1 hoặc 2 đơn giản, trong đó việc giới thiệu EF là quá mức cần thiết trong kịch bản đó.

Nhưng đối với một dự án lớn hơn hoặc nếu dự án của bạn trở nên lớn hơn, tôi thực sự khuyên bạn nên có một lớp DI với khung và tạo khoảng trống để thay đổi khung DI bạn sử dụng (Sử dụng Mặt tiền trong Ứng dụng chính (Ứng dụng web, Api Web, Máy tính để bàn ..Vân vân.).


1

Với một nhà máy, bạn có thể nhóm các giao diện liên quan, vì vậy Nếu các tham số được truyền có thể được nhóm trong một nhà máy thì đó cũng là một giải pháp tốt cho constructor overinjection xem mã này *):

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;
    }

Nhìn vào hàm tạo, bạn chỉ phải truyền vào IAddressModelFactoryđó, nên ít tham số hơn *):

 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,...

Bạn thấy trong CustomerControllerrất nhiều tham số được thông qua, Có bạn có thể thấy điều này nhưconstructor overinjection nhưng đây là cách DI hoạt động. Và không có gì là sai với CustomerController.

*) Mã là từ thương mại điện tử.


1

Nói một cách đơn giản, phương pháp Dependency Injection vs Factory ngụ ý cơ chế đẩy và kéo tương ứng.

Cơ chế kéo : lớp gián tiếp có sự phụ thuộc vào Phương thức xuất xưởng, từ đó có sự phụ thuộc vào các lớp cụ thể.

Cơ chế đẩy : Thành phần gốc có thể được cấu hình với tất cả các thành phần phụ thuộc trong một vị trí và do đó thúc đẩy bảo trì cao và khớp nối lỏng lẻo.

Với trách nhiệm của phương thức Factory vẫn thuộc về lớp (mặc dù là gián tiếp) để tạo ra đối tượng mới, trong đó với việc tiêm phụ thuộc, trách nhiệm đó được thuê ngoài (với chi phí rò rỉ trừu tượng)


0

Tôi nghĩ rằng đây là trực giao và có thể được sử dụng cùng nhau. Hãy để tôi chỉ cho bạn một ví dụ tôi mới bắt gặp tại nơi làm việc:

Chúng tôi đã sử dụng khung công tác Spring trong Java cho DI. Một lớp singleton ( Parent) phải khởi tạo các đối tượng mới của một lớp khác ( Child) và những đối tượng có cộng tác viên phức tạp:

@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);
        // ...
    }
}

Trong ví dụ này, Parentphải nhận các DepXthể hiện chỉ để chuyển chúng đến hàm Childtạo. Vấn đề với điều này:

  1. Parentcó nhiều kiến ​​thức Childhơn nó nên
  2. Parent có nhiều cộng tác viên hơn nó nên
  3. Thêm phụ thuộc để Childthay đổiParent

Đây là khi tôi nhận ra một Factory điều phù hợp ở đây một cách hoàn hảo:

  1. Nó ẩn tất cả nhưng các thông số thực sự của Child lớp, như được thấy bởiParent
  2. Nó gói gọn kiến ​​thức tạo ra một Child , có thể tập trung trong cấu hình DI.

Đây là Parentlớp đơn giản hóa và ChildFactorylớp:

@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

Bạn sử dụng phép nội xạ phụ thuộc khi bạn biết chính xác loại đối tượng bạn yêu cầu tại thời điểm này. Trong khi trong trường hợp mẫu nhà máy, bạn chỉ cần ủy thác quá trình tạo đối tượng cho nhà máy vì bạn không rõ loại đối tượng chính xác mà bạn yêu cầu.


-1

Tôi sử dụng cả hai để tạo ra chiến lược Inversion Of Control với khả năng dễ đọc hơn cho các nhà phát triển, những người cần duy trì nó sau tôi.

Tôi sử dụng Factory để tạo các đối tượng Layer khác nhau (Business, Data Access).

ICarBusiness carBusiness = BusinessFactory.CreateCarBusiness();

Một nhà phát triển khác sẽ thấy điều này và khi tạo một đối tượng Lớp nghiệp vụ, anh ta tìm trong BusinessFactory và Intellisense cung cấp cho nhà phát triển tất cả các Lớp nghiệp vụ có thể để tạo. Không phải chơi trò chơi, hãy tìm Giao diện tôi muốn tạo.

Cấu trúc này đã là Inversion Of Control. Tôi không còn chịu trách nhiệm tạo ra các đối tượng cụ thể. Nhưng bạn vẫn cần đảm bảo Dependency Injection để có thể thay đổi mọi thứ dễ dàng. Tạo Dependency Injection tùy chỉnh của riêng bạn là vô lý, vì vậy tôi sử dụng Unity. Trong CreatCarBusiness () tôi yêu cầu Unity giải quyết lớp nào thuộc về lớp này và nó là trọn đời.

Vì vậy, mã của tôi Cấu trúc tiêm phụ thuộc nhà máy là:

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

Bây giờ tôi có lợi ích của cả hai. Mã của tôi cũng dễ đọc hơn đối với các nhà phát triển khác khi hướng tới phạm vi của các đối tượng tôi sử dụng, thay vì Con Conttor Dependency Injection chỉ nói mọi đối tượng đều khả dụng khi lớp được tạo.

Tôi sử dụng điều này để thay đổi Quyền truy cập dữ liệu cơ sở dữ liệu của mình thành lớp Truy cập dữ liệu được mã hóa tùy chỉnh khi tôi tạo Kiểm tra đơn vị. Tôi không muốn Bài kiểm tra đơn vị của mình giao tiếp với cơ sở dữ liệu, máy chủ web, máy chủ email, v.v. Họ cần kiểm tra Lớp doanh nghiệp của tôi vì đó là nơi có trí thông minh.


-4

Theo quan điểm của tôi, việc sử dụng nội xạ phụ thuộc sẽ tốt hơn nhiều nếu bạn: 1. triển khai mã của mình trong phân vùng nhỏ, vì nó xử lý tốt trong việc tách rời một mã lớn. 2. khả năng kiểm tra là một trong những trường hợp DI sử dụng được vì bạn có thể dễ dàng giả định các đối tượng không tách rời. với việc sử dụng các giao diện, bạn có thể dễ dàng giả định và kiểm tra từng đối tượng. 3. bạn có thể đồng thời sửa đổi từng phần của chương trình mà không cần mã hóa phần khác của chương trình vì nó được tách rời một cách lỏng lẻo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.