Mô hình nhà máy có vi phạm Nguyên tắc mở / đóng không?


13

Tại sao ShapeFactory này sử dụng các câu lệnh có điều kiện để xác định đối tượng nào sẽ khởi tạo. Chúng ta không phải sửa đổi ShapeFactory nếu chúng ta muốn thêm các lớp khác trong tương lai? Tại sao điều này không vi phạm nguyên tắc đóng mở?

Thiết kế mẫu nhà máy

Thiết kế ShapeFactory


2
Mô hình nhà máy nào mà bạn đang đề cập chính xác? Nói chung, một nhà máy là bất kỳ đối tượng hoặc phương thức nào phục vụ để khởi tạo một đối tượng. Sau đó, có các biến thể cụ thể của ý tưởng chung này, chẳng hạn như Mẫu nhà máy trừu tượng, trong đó mỗi trường hợp nhà máy đại diện cho một bảng lựa chọn cụ thể - thường được quản lý thông qua phân lớp thay vì điều kiện.
amon


3
Cảm ơn thông tin đó, điều này làm sáng tỏ rất nhiều. Đó chắc chắn là một ví dụ về mẫu nhà máy, nhưng không phải là Mẫu nhà máy trừu tượng thường được liên kết với mẫu nhà máy. Mã trong bài viết đó là khá nghi vấn, và tôi sẽ không mong đợi để xem bất cứ điều gì như thế trong mã thực.
amon

@ArmonSafai: Bạn đang liên kết bài đăng blog này rất nhiều, nhưng bạn không thực sự giải thích lý do tại sao. Có phải tất cả chúng ta bằng cách nào đó không biết gì về mô hình? Chúng tôi cũng có Google, giống như bạn.
Robert Harvey

1
@RobertHarvey Tôi đang liên kết bài đăng trên blog này để cho biết mô hình nhà máy trong trang đó đang sử dụng điều kiện như thế nào
Armon Safai

Câu trả lời:


20

Sự khôn ngoan hướng đối tượng thông thường là tránh các ifcâu lệnh và thay thế chúng bằng cách gửi động các phương thức được ghi đè trong các lớp con của một lớp trừu tượng. Càng xa càng tốt.

Nhưng quan điểm của mẫu nhà máy là giúp bạn không phải biết về các lớp con riêng lẻ và chỉ làm việc với siêu lớp trừu tượng . Ý tưởng là nhà máy biết rõ hơn bạn về lớp cụ thể nào để khởi tạo và bạn sẽ làm việc tốt hơn chỉ với các phương thức được xuất bản bởi siêu hạng. Điều này thường đúng và là một mô hình có giá trị.

Do đó, không có cách nào để viết một lớp nhà máy có thể từ bỏ các iftuyên bố. Nó sẽ chuyển gánh nặng của việc chọn một lớp cụ thể cho người gọi, đó chính xác là những gì mô hình được cho là nên tránh. Không phải tất cả các nguyên tắc là tuyệt đối (trên thực tế, không có nguyên tắc nào là tuyệt đối) và nếu bạn sử dụng mẫu này, bạn sẽ cho rằng lợi ích từ nó lớn hơn lợi ích của việc không sử dụng if.


2
Hoàn toàn có thể tạo ra một mô hình nhà máy mà không cần nhiều ifs. Xem câu trả lời của @ BЈовић để biết ví dụ đơn giản về cách đạt được điều này. Bị hạ bệ.
David Arno


11
@DavidArno Đương nhiên, có nhiều cách khác nhau để chọn lớp cụ thể. Bộ định vị dịch vụ là một, Bộ chứa IoC có thể định cấu hình là một bộ khác. Đó chỉ là chi tiết thực hiện; họ không làm mất đi thông điệp chính của Killian, đó là Nhà máy giải phóng người gọi khỏi việc phải quyết định lớp cụ thể nào để khởi tạo. Đừng để bị sa lầy vào các chi tiết.
Robert Harvey

1
Tuyên bố tuyệt vời mà không có cách nào trả lời câu hỏi.
Martin Maat

1
@ R.Schmitz Tôi nghĩ bạn không đúng trong giả định của mình. Tôi nghĩ rằng nhiều người đã bỏ lỡ câu hỏi này từ OP: "Chúng ta không phải sửa đổi ShapeFactory nếu chúng ta muốn thêm các lớp khác trong tương lai?" RAR RÀNG, OP bối rối khi nghĩ rằng mẫu này vi phạm OCP vì để thêm chức năng mới, bạn phải sửa đổi mã hiện có. Câu trả lời chính xác cho câu hỏi này có thể được tìm thấy trong câu trả lời của tôi. Câu trả lời ngắn gọn: Bạn để mã đó một mình và bạn áp dụng mẫu Tóm tắt Factory cho EXTEND (không sửa đổi) chức năng hiện có của bạn. Vì câu trả lời này của Kilian KHÔNG giải quyết câu hỏi.
hfontanez

4

Ví dụ có thể sử dụng một tuyên bố có điều kiện bởi vì nó là đơn giản nhất. Việc triển khai phức tạp hơn có thể sử dụng bản đồ hoặc cấu hình hoặc (nếu bạn muốn thực sự ưa thích) một số loại đăng ký mà các lớp có thể tự đăng ký. Tuy nhiên, không có gì sai khi sử dụng một điều kiện nếu số lượng lớp nhỏ và thay đổi không thường xuyên.

Mở rộng điều kiện để thêm hỗ trợ cho một lớp con mới trong tương lai thực sự sẽ nói đúng là vi phạm nguyên tắc mở / đóng. Giải pháp "chính xác" sẽ là tạo ra một nhà máy mới có cùng giao diện. Điều đó nói rằng, việc tuân thủ nguyên tắc O / C phải luôn được cân nhắc với các nguyên tắc thiết kế khác như KISS và YAGNI.

Điều đó nói rằng, mã được hiển thị là mã ví dụ rõ ràng được thiết kế để hiển thị khái niệm về một nhà máy và không có gì khác. Ví dụ, phong cách thực sự xấu khi trả về null như ví dụ, nhưng xử lý lỗi phức tạp hơn sẽ chỉ làm mờ điểm. Mã ví dụ không phải là mã chất lượng sản xuất, bất kỳ mã nào bạn không nên mong đợi.


Bạn có thể giải thích làm thế nào một bản đồ / cấu hình / đăng ký sẽ làm việc?
Armon Safai

@ArmonSafai: Dưới đây là một ví dụ: jkfill.com/2010/12/29/self-registering-factories-in-c-sharp
JacquesB

Các nhà máy tự đăng ký, AFAIK, không thể có trong C ++ bên trong các thư viện tĩnh do các biến toàn cục không được sử dụng (nghĩa là sử dụng odr) bị loại bỏ bởi các công cụ.
void.pulum

@ArmonSafai đọc phần này để hiểu thêm về goo.gl/RYuNSM
AZ_

2

Bản thân mẫu không vi phạm Nguyên tắc Mở / Đóng (OCP). Tuy nhiên, chúng tôi vi phạm OCP khi chúng tôi sử dụng mẫu không chính xác.

Câu trả lời đơn giản cho câu hỏi này như sau:

  1. Tạo chức năng cơ bản của bạn bằng cách sử dụng Mô hình phương thức nhà máy .
  2. NGOẠI TRỪ chức năng của bạn bằng cách sử dụng Mẫu nhà máy trừu tượng

Trong ví dụ được cung cấp, chức năng cơ sở của bạn hỗ trợ ba hình dạng: Hình tròn, Hình chữ nhật và Hình vuông. Giả sử bạn cần hỗ trợ Tam giác, Lầu năm góc và Lục giác trong tương lai. Để làm điều này mà KHÔNG vi phạm OCP, bạn phải tạo một nhà máy bổ sung để hỗ trợ các hình dạng mới của bạn (hãy gọi AdvancedShapeFactory) và sau đó sử dụng AbstractFactory để quyết định nhà máy nào bạn cần tạo để tạo bất kỳ hình dạng nào bạn cần.


Tôi rất thích giải pháp nhà máy tự đăng ký (trong trường hợp không có bộ chứa IoC có thể cấu hình thực sự là tốt nhất), vì nếu không, những gì chúng tôi nhận được từ đề xuất của bạn về cơ bản là các nhà máy của nhà máy , và đó là khi mọi thứ trở nên quá phức tạp.
Nom1fan

1

Nếu bạn đang nói về mẫu Tóm tắt của Nhà máy, việc đưa ra quyết định thường không nằm trong chính Nhà máy mà là mã ứng dụng. Đó là mã chọn nhà máy cụ thể để khởi tạo và chuyển qua mã máy khách sẽ sử dụng các đối tượng do Nhà máy sản xuất. Xem phần cuối của ví dụ Java tại đây: https://en.wikipedia.org/wiki/ Ab khu_factory_potype

Ra quyết định không nhất thiết ngụ ý iftuyên bố. Nó có thể đọc loại Factory Factory cụ thể từ một tệp cấu hình, xuất phát từ cấu trúc bản đồ, v.v.



Nếu tôi, người gọi, đang đưa ra quyết định loại lớp cụ thể nào, thì tại sao tôi lại bận tâm với một Nhà máy Trừu tượng?
Robert Harvey

Vui lòng xác định "người gọi". Như tôi mô tả trong câu trả lời của mình, có mã ứng dụng toàn cầu và sau đó có mã cần sinh ra các đối tượng bằng cách sử dụng Factory. Trong khi sau này thực sự cần phải được nhận thức của lớp bê tông để nhanh chóng, một số mã khác theo ngữ cảnh biết về nó và mới nó lên ...
guillaume31

0

Nếu bạn nghĩ về Open-Close ở cấp độ lớp với nhà máy này, thì bạn đang tạo một lớp khác trong hệ thống của bạn Open-Close, ví dụ nếu bạn có một lớp khác lấy một Hình dạng và tính diện tích (ví dụ điển hình) thì lớp này là OpenClose vì nó có thể tính diện tích cho các loại hình mới mà không cần sửa đổi. Sau đó, bạn có một lớp khác vẽ hình dạng, một lớp khác có hình dạng N và trả về hình lớn hơn và bạn có thể nghĩ chung rằng các lớp khác trong hệ thống của bạn có hình dạng là Open-Close (ít nhất là về hình dạng). Nhìn vào thiết kế, nhà máy cho phép phần còn lại của hệ thống là Open-Close và tất nhiên là chính nhà máy ITS KHÔNG Open-Close.

Dĩ nhiên, bạn cũng có thể làm cho nhà máy này đóng mở, thông qua một số loại tải động và toàn bộ hệ thống của bạn có thể là Đóng mở (ví dụ, bạn có thể thêm hình dạng mới thả một số jar trong đường dẫn lớp). Bạn cần đánh giá mức độ phức tạp thêm này có giá trị tùy thuộc vào hệ thống mà bạn đang xây dựng, không phải tất cả các hệ thống đều cần các tính năng có thể cắm và không phải tất cả hệ thống đều cần phải đóng mở hoàn toàn.


0

Nguyên tắc đóng mở, như nguyên tắc thay thế Liskov, áp dụng cho các cây lớp, cho hệ thống phân cấp thừa kế. Trong ví dụ của bạn, lớp nhà máy không nằm trong cây gia đình của các lớp mà nó khởi tạo để nó không thể vi phạm các quy tắc này. Sẽ có vi phạm nếu GetShape của bạn (hoặc được đặt tên thích hợp hơn là CreatShape) được triển khai trong lớp cơ sở Shape.


-2

Tất cả phụ thuộc vào cách bạn thực hiện nó. Bạn có thể sử dụng std::mapđể giữ các con trỏ hàm đến các hàm tạo đối tượng. Sau đó, nguyên tắc mở / đóng không bị vi phạm. Hoặc chuyển đổi / trường hợp.

Dù sao, nếu bạn không thích mẫu nhà máy, bạn luôn có thể sử dụng phương pháp tiêm phụ thuộc.


6
Làm thế nào là chuyển đổi / trường hợp tốt hơn so với điều kiện? Sử dụng bản đồ / dict / bảng để thể hiện mã dưới dạng dữ liệu là tốt, nếu bạn thực sự cần một sổ đăng ký thực hiện khác nhau - ví dụ như trong một số triển khai container DI. Nhưng có các cuộc gọi lại khác nhau cùng loại là không cần thiết cho hầu hết các nhà máy! Tôi không hiểu tại sao bạn lại gợi ý như vậy. Ngoài ra, nhiều container DI được triển khai theo các đối tượng của nhà máy, do đó, đề xuất sử dụng DI thay vì các nhà máy có vẻ hơi tròn.
amon

1
@amon Ý tôi là dùng các loại DI khác - không phải nhà máy.
BЈовић


1
Nhà máy của bạn sẽ quyết định sử dụng con trỏ nào? Cuối cùng, bạn phải đưa ra quyết định.
whatsisname

@ArmonSafai ???
BЈовић
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.