2009-03-05 37 views
7

Một trong những cách để triển khai Dependency Injection chính xác là tạo riêng đối tượng từ logic nghiệp vụ. Thông thường, điều này liên quan đến việc sử dụng một Nhà máy để tạo đối tượng.Có thể chuyển thông số sang phương thức Factory không?

Cho đến thời điểm này, tôi chưa bao giờ nghiêm túc xem xét sử dụng một máy vì vậy tôi xin lỗi nếu câu hỏi này có vẻ hơi đơn giản:

Trong tất cả các ví dụ về các mẫu máy mà tôi đã chạy ngang qua, tôi luôn xem các ví dụ rất đơn giản không có tham số. Ví dụ: đây là một Nhà máy bị đánh cắp từ Misko Hevery's xuất sắc How To Think About the "new" Operator bài viết.

 
class ApplicationBuilder { 
    House build() { 
    return new House(new Kitchen(
       new Sink(), 
       new Dishwasher(), 
       new Refrigerator()) 
      ); 
    } 
} 

Tuy nhiên, điều gì sẽ xảy ra nếu tôi muốn mỗi ngôi nhà mà tôi xây dựng để có tên? Tôi vẫn đang sử dụng mẫu Nhà máy nếu tôi viết lại mã này như sau?

 
class ApplicationBuilder { 
    House build(const std::string & house_name) { 
    return new House(house_name, 
         new Kitchen(new Sink(), 
            new Dishwasher(), 
            new Refrigerator()) 
        ); 
    } 
} 

Lưu ý rằng lời gọi phương thức Nhà máy của tôi đã thay đổi từ này:

 
ApplicationBuilder builder; 
House * my_house = builder.build();

Để này:

 
ApplicationBuilder builder; 
House * my_house = builder.build("Michaels-Treehouse");

Bằng cách này: Tôi nghĩ rằng khái niệm tách đối tượng instantiating từ logic kinh doanh thật tuyệt, tôi chỉ cố gắng tìm ra cách tôi có thể áp dụng nó vào tình huống của chính mình. Điều làm tôi bối rối là tất cả các ví dụ mà tôi đã thấy về mẫu Factory không bao giờ chuyển bất kỳ tham số nào vào hàm build().

Để rõ ràng: Tôi không biết tên của ngôi nhà cho đến thời điểm này trước khi tôi cần phải khởi tạo nó.

+0

Tôi thường thấy các nhà máy lấy thông số như bạn đã hiển thị. Không có gì là sai với nó. Hoặc về mặt câu hỏi của bạn. Nó là hoàn toàn ok. – grieve

Trả lời

8

Tôi đã nhìn thấy khá nhiều ví dụ sử dụng một tập hợp các đối số cố định, như trong ví dụ tên của bạn và đã tự sử dụng chúng và tôi không thể thấy có gì sai với nó. Tuy nhiên, có một lý do chính đáng là nhiều hướng dẫn hoặc bài viết nhỏ tránh hiển thị các nhà máy chuyển tiếp các tham số tới các đối tượng đã xây dựng: thực tế không thể chuyển tiếp số lượng đối số tùy ý (ngay cả đối với giới hạn sane như 6 đối số). Mỗi thông số bạn chuyển tiếp phải được chấp nhận là const T&T& nếu bạn muốn thực hiện chung. Tuy nhiên, đối với các ví dụ phức tạp hơn, bạn cần một tập quá tải ngày càng tăng theo cấp số nhân (đối với mỗi tham số, một phiên bản không phải là phiên bản nonconst) và perfect forwarding không thể thực hiện được (ví dụ như tạm thời được chuyển tiếp thành thời gian tạm thời). . Đối với C++ chuẩn tiếp theo vấn đề đó được giải quyết:

class ApplicationBuilder { 
    template<typename... T> 
    House *build(T&&... t) { 
    return new House(std::forward<T>(t)..., 
         new Kitchen(new Sink(), 
            new Dishwasher(), 
            new Refrigerator()) 
        ); 
    } 
}; 

Bằng cách đó, bạn có thể gọi

builder.build("Hello", 13); 

Và nó sẽ trở lại

new House("Hello", 13, new Kitchen(new Sink(... 

Đọc bài viết tôi liên kết ở trên.

+0

Liên kết tốt đẹp về tài liệu tham khảo rvalue! Nó có lẽ nằm ngoài phạm vi của câu hỏi, nhưng nó là một trong những bài viết rõ ràng nhất mà tôi đã đọc về chủ đề này. –

5

Tôi không thể thấy tại sao việc thêm thông số này vào nhà máy của bạn sẽ sai. Nhưng lưu ý rằng bạn không nên kết thúc việc thêm nhiều tham số có thể không hữu ích cho tất cả các đối tượng được tạo bởi nhà máy. Nếu bạn làm thế, bạn sẽ mất khá nhiều lợi thế của một nhà máy!

+0

Bằng cách mô tả loại đối tượng bạn muốn (trong params), điều này có thể cho phép nhà máy trả về các đối tượng hoàn toàn khác nhau (bằng cách trả về một lớp cơ sở hoặc một giao diện). – Aardvark

1

Tôi đồng ý với Benoit. Hãy nghĩ về một nhà máy để tạo ra một cái gì đó giống như các kết nối sql, trong một trường hợp như thế này, nó sẽ là cần thiết để truyền thông tin về kết nối đến nhà máy. Nhà máy sẽ sử dụng thông tin đó để sử dụng đúng giao thức máy chủ và như vậy.

4

Ý tưởng về một nhà máy là nó cung cấp cho bạn một thể hiện của một lớp/giao diện, vì vậy không có gì sai với các tham số truyền. Nếu có, nó sẽ là xấu để vượt qua các tham số cho một() mới là tốt.

5

Không chỉ chấp nhận được, mà còn là phổ biến để chuyển các tham số cho phương thức nhà máy. Kiểm tra some examples. Thông thường, tham số là một kiểu báo cho nhà máy biết phải làm gì, nhưng không có lý do gì bạn không thể thêm thông tin khác mà bạn cần để xây dựng một đối tượng. Tôi nghĩ rằng những gì bạn đang làm là tốt.

1

Chắc chắn, tại sao không ..!?

Điều tốt đẹp về các tham số truyền là nó cho phép bạn ẩn việc triển khai đối tượng cụ thể. Ví dụ: trong mã bạn đã đăng, bạn chuyển các tham số cho hàm tạo. Tuy nhiên, bạn có thể thay đổi cách triển khai để chúng được chuyển qua phương thức Initiailze. Bằng cách chuyển các tham số cho phương thức factory bạn ẩn bản chất của việc xây dựng và khởi tạo đối tượng từ người gọi.

1

Hãy xem Loki :: Nhà máy, có một quá trình triển khai thực hiện rất giống với tính năng Boost. Một số mã ví dụ tôi thường xuyên sử dụng trong hương vị khác nhau:

typedef Loki :: SingletonHolder < Loki :: Nhà máy < Component, std :: string, Loki :: Typelist < const DataCollection &, Loki :: Typelist < game *, Loki :: NullType>>>> ComponentFactory;

Điều này có vẻ hơi kỳ lạ ngay từ cái nhìn đầu tiên, tuy nhiên hãy để tôi giải thích về con thú này và nó thực sự mạnh đến mức nào. Về cơ bản chúng tôi tạo ra một singleton chứa một nhà máy, các tham số nhất là cho singleton, Component là sản phẩm của chúng tôi, std :: string là loại id tạo của chúng tôi, sau này sau một danh sách loại params cần thiết để tạo thành phần (điều này có thể được định nghĩa bằng cách sử dụng macro cũng cho cú pháp ít tiết). Sau dòng này, bạn chỉ có thể thực hiện:

ComponentFactory :: Instance(). CreateObject ("someStringAssociatedWithConcreteType", anDataCollection, aGamePointer);

Để tạo đối tượng, hãy đăng ký một đối tượng chỉ sử dụng ComponentFactory :: Instance(). Đăng ký() ;. Có một chương tuyệt vời về các chi tiết trong cuốn sách Modern C++ Design.

Các vấn đề liên quan