Cách sử dụng Dependency Injection kết hợp với mẫu Factory


11

Xem xét một mô-đun chịu trách nhiệm phân tích cú pháp các tệp thuộc bất kỳ loại nào. Tôi đang nghĩ đến việc sử dụng mô hình chiến lược để giải quyết vấn đề này vì tôi đã giải thích ở đây . Vui lòng tham khảo bài viết được liên kết trước khi tiếp tục với câu hỏi này.

Hãy xem xét lớp B cần nội dung của tệp sản phẩm. Lớp này sẽ cần khởi tạo trình triển khai cụ thể thích hợp của giao diện Parser để phân tích tệp XML. Tôi có thể ủy thác việc khởi tạo của người triển khai cụ thể thích hợp cho một Nhà máy sao cho lớp B "có một" Nhà máy. Tuy nhiên, lớp B sau đó sẽ "phụ thuộc" vào Nhà máy để khởi tạo người triển khai cụ thể. Điều này có nghĩa là hàm tạo hoặc phương thức setter trong lớp B sẽ cần phải được thông qua Factory.

Do đó, Nhà máy và lớp B cần phân tích một tệp sẽ được liên kết chặt chẽ với nhau. Tôi hiểu rằng tôi có thể hoàn toàn sai về bất cứ điều gì tôi đã giải thích cho đến nay. Tôi muốn biết liệu tôi có thể sử dụng phép tiêm phụ thuộc trong kịch bản mà phụ thuộc được tiêm là Nhà máy hay không và cách nào là đúng để thực hiện việc này để tôi có thể tận dụng các lĩnh vực như chế tạo Nhà máy trong các thử nghiệm đơn vị của mình.

Câu trả lời:


8

Cách đúng đắn để làm điều này là phụ thuộc vào một giao diện, và sau đó đưa việc triển khai giao diện đó vào Lớp B.

Một giao diện chỉ là thứ mỏng nhất mà bạn có thể phụ thuộc vào - tôi ví nó giống như cố gắng lấy một làn khói. Mã phải kết hợp với một cái gì đó nếu không nó sẽ không làm được gì, nhưng việc ghép nối với một giao diện sẽ tách rời như bạn có thể nhận được, nhưng các giao diện có thể cung cấp tất cả các chức năng mà bạn muốn.

Vì vậy, hàm tạo của Lớp B đưa giao diện đến lớp mà nó cần và yêu cầu nhà máy sản xuất lớp đó với tư cách là người triển khai giao diện. Đừng phụ thuộc vào nhà máy, phụ thuộc vào giao diện và nhà máy cung cấp việc triển khai nhà máy đó.

Vì vậy, có, bạn sẽ sử dụng tiêm phụ thuộc, nhưng không có gì sai với điều đó. Tiêm phụ thuộc - tiêm đặc biệt đơn giản - nên là cách làm "bình thường". Đơn giản chỉ cần tắt những thứ mới trong ứng dụng của bạn (và càng gần dòng đầu tiên main) càng tốt và ẩn cuộc gọi mới trong một lớp được thiết kế dành riêng cho việc tạo ra mọi thứ.

Điểm mấu chốt: Đừng ngần ngại tiêm phụ thuộc. Đó nên là cách làm bình thường.


Liệu constructor tiêm đưa ra những thứ mới càng lâu càng tốt?
JeffO

Điều đó thật tồi tệ - Tôi đã sửa nó để nói "càng lùi xa trong ứng dụng của bạn càng tốt".
Nick Hodges

Đúng. Tôi đã suy nghĩ phụ thuộc vào giao diện Parser thay vì một nhà máy. Bây giờ nếu một lớp khác sử dụng lớp B, nó sẽ phải phụ thuộc vào giao diện mà lớp B phụ thuộc vào. Điều này sẽ tiếp tục tiếp tục cho đến lớp cấp cao nhất. Lớp cấp cao nhất này cuối cùng sẽ phải sử dụng một nhà máy để khởi tạo thể hiện cụ thể thích hợp của Trình phân tích cú pháp. Lớp cấp cao nhất có nên phụ thuộc vào một nhà máy hay nên sử dụng nhà máy trực tiếp bên trong các phương thức của nó?
CKing

1
Nói tốt: Đừng ngần ngại tiêm phụ thuộc
Signcodeindie

9

Tôi nghĩ tiền đề của bạn có một chút nhầm lẫn ở đây, bạn nói về việc tiêm một nhà máy, nhưng mô hình nhà máy là một mô hình sáng tạo với mục đích là làm một tập hợp con của khung công tác phụ thuộc, khi các khung DI không phổ biến mô hình này hữu ích cho lý do đó. Tuy nhiên, nếu bạn có khung DI, bạn không còn thực sự cần một nhà máy nữa vì khung DI có thể hoàn thành mục đích mà nhà máy sẽ hoàn thành.

Điều đó nói rằng, hãy để tôi giải thích một chút về tiêm phụ thuộc và cách bạn thường sử dụng nó.

Có nhiều cách khác nhau để thực hiện tiêm phụ thuộc, nhưng phổ biến nhất là tiêm xây dựng, tiêm tài sản và DIContainer trực tiếp. Tôi sẽ nói về việc tiêm nhà xây dựng vì việc tiêm tài sản là cách tiếp cận sai trong hầu hết thời gian (cách tiếp cận đúng thời gian) và không nên truy cập DIContainer trừ khi bạn hoàn toàn không thể thực hiện một trong hai cách tiếp cận khác.

Trình xây dựng tiêm là nơi bạn có giao diện cho một phụ thuộc và DIContainer (hoặc nhà máy) biết triển khai cụ thể cho phụ thuộc đó và bất cứ khi nào bạn cần một đối tượng phụ thuộc vào giao diện đó, tại thời điểm xây dựng bạn giao việc triển khai từ nhà máy để nó

I E

IDbConnectionProvider connProvider = DIContainer.Get<IDbConnectionProvider>();
IUserRepository userRepo = new UserRepository(connProvider);
User currentUser = userRepo.GetCurrentUser();

Nhiều khung DI có thể đơn giản hóa điều này một cách đáng kể đến nơi DIContainer của bạn sẽ kiểm tra hàm tạo của UserRep repository để biết các giao diện mà nó biết triển khai cụ thể và sẽ tự động trao chúng cho bạn; kỹ thuật này thường được gọi là Inversion of Control, mặc dù DI và IoC đều là hai thuật ngữ được thay thế cho nhau rất nhiều và có sự khác biệt mơ hồ (nếu có).

Bây giờ nếu bạn đang tự hỏi làm thế nào mã bao trùm truy cập vào DIContainer, thì bạn cũng có thể có một lớp tĩnh để truy cập nó hoặc điều thích hợp hơn là hầu hết các khung DI cho phép bạn tạo một DIContainer mới, trong đó nó thực sự sẽ hoạt động như một trình bao bọc cho một từ điển singleton nội bộ cho các loại mà nó biết là cụ thể cho các giao diện nhất định.

Điều đó có nghĩa là, bạn có thể cập nhật DIContainer bất cứ nơi nào bạn muốn trong mã và nhận được cùng một DIContainer mà bạn đã cấu hình để biết các mối quan hệ giao diện cụ thể của mình. Các phương tiện thông thường để ẩn DIContainer khỏi các phần của mã không nên tương tác trực tiếp với nó là chỉ đơn giản là đảm bảo chỉ (các) dự án cần thiết có tham chiếu đến khung DI.


Tôi không hoàn toàn đồng ý với đoạn đầu tiên. Vẫn có những kịch bản mà bạn phụ thuộc vào một nhà máy (thực hiện không gian) được tiêm bởi container DI.
Piotr Perak

Tôi nghĩ rằng bạn đã bỏ lỡ điểm vì OP không nói về khung DI hoặc container DI.
Doc Brown

@Jimmy Hoffa Trong khi bạn đưa ra một số điểm rất thú vị, tôi biết về các container IoC như Spring và cconcept của Dependency Injection. Mối quan tâm của tôi liên quan đến một kịch bản trong đó bạn không sử dụng khung IoC trong trường hợp đó, bạn cần phải viết một lớp sẽ khởi tạo các phụ thuộc của bạn cho bạn. Nếu lớp A phụ thuộc vào giao diện B và lớp C sử dụng lớp A, thì lớp C phụ thuộc vào giao diện B. Ai đó cần cung cấp cho lớp C một lớp B. Nên lớp C sau đó phụ thuộc vào sự
kín đáo

Nếu lớp A phụ thuộc vào giao diện B và lớp C sử dụng lớp A, thì lớp C phụ thuộc vào giao diện B. Ai đó cần cung cấp cho lớp C một giao diện B. Nên lớp C sau đó phụ thuộc vào giao diện B hay nên phụ thuộc vào lớp đã khởi tạo phụ thuộc Tức là nhà máy. Có vẻ như bạn đã trả lời câu hỏi này trong câu trả lời của mình nhưng với tình trạng quá tải thông tin :)
CKing

@bot như tôi đã nói ở lời nói đầu, phần lớn bạn có thể coi các nhà máy là một tập hợp con của chức năng của một thùng chứa DI, điều đó không đúng trong mọi tình huống như Doc Brown đã đề cập, nhưng hãy xem mẫu mã của tôi và thay thế DIContainerbằng DbConnectionFactorykhái niệm vẫn đứng vững rằng bạn sẽ truy xuất triển khai cụ thể từ DI / Factory / etc của bạn và giao nó cho người tiêu dùng loại tại thời điểm xây dựng của họ.
Jimmy Hoffa

5

Bạn có thể vượt qua một nhà máy thông qua việc tiêm phụ thuộc giống như bạn vượt qua bất cứ điều gì khác, đừng để sự đệ quy của tình huống làm bạn bối rối. Tôi không biết nói gì khác về việc thực hiện nó - bạn đã biết cách thực hiện tiêm phụ thuộc.

Tôi sử dụng DI để tiêm nhà máy khá thường xuyên.


1
Nếu một lớp phụ thuộc vào một Nhà máy, bạn sẽ cần phải chế nhạo nhà máy khi đơn vị kiểm tra lớp đó. Làm thế nào để bạn đi về làm điều đó? Hãy để lớp học phụ thuộc vào một giao diện nhà máy?
CKing

4

Không có gì sai trong các nhà máy tiêm. Đó là cách tiêu chuẩn nếu bạn không thể quyết định trong quá trình xây dựng đối tượng 'cha mẹ', bạn sẽ cần loại phụ thuộc nào. Tôi nghĩ ví dụ sẽ giải thích nó tốt nhất. Tôi sẽ sử dụng c # vì tôi không biết java.


class Parent
{
    private IParserFactory parserFactory;
    public Parent(IParserFactory factory)
    {
        parserFactory = factory
    }

    public ParsedObject ParseFrom(string filename, FileType fileType)
    {
        var parser = parserFactory.CreateFor(fileType);
        return parser.Parse(filename);
    }
}
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.