Nói rằng tôi có một lớp A tạo ra lớp B. Lớp B phụ thuộc vào lớp C và lớp C phụ thuộc vào lớp D. Ai phải chịu trách nhiệm tạo lớp D?
Bạn đang nhảy các bước. Xem xét một tập hợp các quy ước được tối ưu hóa cho khớp nối lỏng lẻo và an toàn ngoại lệ. Các quy tắc đi như thế này:
R 1: nếu A chứa B, thì hàm tạo của A nhận B được xây dựng hoàn chỉnh (nghĩa là không phải "phụ thuộc xây dựng của B"). Tương tự, nếu cấu trúc của B yêu cầu C, nó sẽ nhận được C, không phụ thuộc vào C.
R2: nếu một chuỗi các đối tượng đầy đủ được yêu cầu để xây dựng một đối tượng, việc xây dựng chuỗi được trích xuất / tự động trong một nhà máy (chức năng hoặc lớp).
Mã (std :: di chuyển cuộc gọi bị bỏ qua cho đơn giản):
struct D { int dummy; };
struct C { D d; };
struct B { C c; }
struct A { B make_b(C c) {return B{c}; };
Trong một hệ thống như vậy, "người tạo D" là không liên quan, bởi vì khi bạn gọi make_b, bạn cần có C, không phải D.
Mã khách hàng:
A a; // factory instance
// construct a B instance:
D d;
C c {d};
B = a.make_b(c);
Ở đây, D được tạo bởi mã máy khách. Đương nhiên, nếu mã này được lặp lại nhiều lần, bạn có thể trích xuất nó thành một hàm (xem R2 ở trên):
B make_b_from_d(D& d) // you should probably inject A instance here as well
{
C c {d};
A a;
return a.make_b(c);
}
Có một xu hướng tự nhiên là bỏ qua định nghĩa của make_b (bỏ qua R1 ) và viết mã trực tiếp như thế này:
struct D { int dummy; };
struct C { D d; };
struct B { C c; }
struct A { B make_b(D d) { C c; return B{c}; }; // make B from D directly
Trong trường hợp này, bạn có các vấn đề sau:
bạn có mã nguyên khối; Nếu bạn gặp tình huống trong mã máy khách nơi bạn cần tạo B từ C tồn tại, bạn không thể sử dụng make_b. Bạn sẽ cần phải viết một nhà máy mới hoặc định nghĩa của make_b và tất cả mã máy khách bằng cách sử dụng make_b cũ.
Quan điểm về sự phụ thuộc của bạn bị rối loạn khi bạn nhìn vào nguồn: Bây giờ, bằng cách nhìn vào nguồn bạn có thể nghĩ rằng bạn cần một cá thể D, trong khi thực tế bạn có thể chỉ cần một C.
Thí dụ:
void sub_optimal_solution(C& existent_c) {
// you cannot create a B here using existent_C, because your A::make_b
// takes a D parameter; B's construction doesn't actually need a D
// but you cannot see that at all if you just have:
// struct A { B make_b(D d); };
}
- Việc bỏ qua
struct A { B make_b(C c); }
sẽ làm tăng đáng kể sự ghép đôi: bây giờ A cần biết các định nghĩa của cả B và C (thay vì chỉ C). Bạn cũng có các hạn chế đối với bất kỳ mã máy khách nào sử dụng A, B, C và D, áp đặt cho dự án của bạn vì bạn đã bỏ qua một bước trong định nghĩa của phương thức xuất xưởng ( R1 ).
TLDR: Tóm lại, không vượt qua sự phụ thuộc ngoài cùng cho một nhà máy, mà là những người gần nhất. Điều này làm cho mã của bạn mạnh mẽ, dễ dàng thay đổi và biến câu hỏi bạn đặt ra ("ai tạo ra D") thành một câu hỏi không liên quan để thực hiện make_b (vì make_b không còn nhận được D mà là phụ thuộc ngay lập tức hơn - C - và đây là được tiêm dưới dạng tham số của make_b).