Khi nào thì nên sử dụng các phương thức Factory trong một đối tượng thay vì lớp Factory?
Khi nào thì nên sử dụng các phương thức Factory trong một đối tượng thay vì lớp Factory?
Câu trả lời:
Tôi thích suy nghĩ về các thiết kế vỗ về các lớp của tôi là "con người", và các mẫu là cách mọi người nói chuyện với nhau.
Vì vậy, với tôi mô hình nhà máy giống như một công ty tuyển dụng. Bạn đã có ai đó sẽ cần một số lượng công nhân khác nhau. Người này có thể biết một số thông tin họ cần ở những người họ thuê, nhưng đó là thông tin đó.
Vì vậy, khi họ cần một nhân viên mới, họ gọi cho cơ quan tuyển dụng và nói với họ những gì họ cần. Bây giờ, để thực sự thuê một ai đó, bạn cần biết rất nhiều thứ - lợi ích, xác minh đủ điều kiện, v.v. Nhưng người tuyển dụng không cần biết gì về điều này - cơ quan tuyển dụng xử lý tất cả những điều đó.
Theo cách tương tự, sử dụng Factory cho phép người tiêu dùng tạo các đối tượng mới mà không cần phải biết chi tiết về cách họ tạo ra hoặc phụ thuộc của họ là gì - họ chỉ phải cung cấp thông tin họ thực sự muốn.
public interface IThingFactory
{
Thing GetThing(string theString);
}
public class ThingFactory : IThingFactory
{
public Thing GetThing(string theString)
{
return new Thing(theString, firstDependency, secondDependency);
}
}
Vì vậy, bây giờ người tiêu dùng của ThingFactory có thể nhận được một Điều, mà không cần phải biết về sự phụ thuộc của Điều đó, ngoại trừ dữ liệu chuỗi đến từ người tiêu dùng.
within an object instead of a Factory class
. Tôi nghĩ rằng anh ta có nghĩa là kịch bản nơi bạn đặt ctor riêng tư và sử dụng một phương thức tĩnh để khởi tạo lớp (tạo một đối tượng). Nhưng để làm theo ví dụ này, người ta phải khởi tạo ThingFactory
lớp trước để lấy Thing
các đối tượng, điều này làm cho điều này Factory class
có hiệu lực.
Các phương thức của nhà máy nên được coi là một giải pháp thay thế cho các nhà xây dựng - chủ yếu là khi các nhà xây dựng không đủ biểu cảm, tức là.
class Foo{
public Foo(bool withBar);
}
không biểu cảm như:
class Foo{
public static Foo withBar();
public static Foo withoutBar();
}
Các lớp Factory rất hữu ích khi bạn cần một quy trình phức tạp để xây dựng đối tượng, khi xây dựng cần một phụ thuộc mà bạn không muốn cho lớp thực tế, khi bạn cần xây dựng các đối tượng khác nhau, v.v.
Một tình huống mà cá nhân tôi tìm thấy các lớp Factory riêng biệt có ý nghĩa là khi đối tượng cuối cùng bạn đang cố gắng tạo ra dựa vào một số đối tượng khác. Ví dụ, trong PHP: Giả sử bạn có một House
đối tượng, lần lượt có một Kitchen
và một LivingRoom
đối tượng và LivingRoom
đối tượng cũng có một TV
đối tượng bên trong.
Phương pháp đơn giản nhất để đạt được điều này là yêu cầu mỗi đối tượng tạo con cái trên phương thức xây dựng của chúng, nhưng nếu các thuộc tính được lồng tương đối, khi bạn House
không tạo được, bạn có thể sẽ mất một thời gian để cố gắng cách ly chính xác những gì đang thất bại.
Cách khác là làm như sau (tiêm phụ thuộc, nếu bạn thích thuật ngữ ưa thích):
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
Ở đây, nếu quá trình tạo ra một House
thất bại chỉ có một nơi để tìm, nhưng việc phải sử dụng đoạn này mỗi khi người ta muốn một cái mới House
thì không thuận tiện. Nhập các nhà máy:
class HouseFactory {
public function create() {
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
return $HouseObj;
}
}
$houseFactory = new HouseFactory();
$HouseObj = $houseFactory->create();
Nhờ vào nhà máy ở đây, quá trình tạo ra một House
bản tóm tắt (trong đó bạn không cần phải tạo và thiết lập mọi phụ thuộc duy nhất khi bạn chỉ muốn tạo một House
) và đồng thời tập trung hóa để giúp duy trì dễ dàng hơn. Có nhiều lý do khác tại sao sử dụng các Nhà máy riêng biệt có thể có lợi (ví dụ: khả năng kiểm tra) nhưng tôi thấy trường hợp sử dụng cụ thể này để minh họa rõ nhất cách các lớp Factory có thể hữu ích.
HouseFactory
lớp?
create
phương thức. Ví dụ, nếu bạn House
sẽ luôn có cùng một loại LivingRoom
thì có thể có các thông số được mã hóa cứng trong lớp nhà máy thay vì được truyền dưới dạng đối số. Hoặc bạn có thể muốn cung cấp một type
đối số cho HouseFactory::create
phương thức của mình nếu bạn có một vài loại LivingRoom
s và có một công tắc bên trong với các tham số được mã hóa cứng cho từng loại.
Điều quan trọng là phải phân biệt rõ ràng ý tưởng đằng sau việc sử dụng phương pháp nhà máy hoặc nhà máy. Cả hai đều nhằm giải quyết các loại vấn đề tạo đối tượng loại trừ lẫn nhau.
Hãy nói cụ thể về "phương pháp nhà máy":
Điều đầu tiên là, khi bạn đang phát triển thư viện hoặc API, lần lượt sẽ được sử dụng để phát triển ứng dụng, thì phương thức xuất xưởng là một trong những lựa chọn tốt nhất cho mẫu tạo. Lý do đằng sau; Chúng tôi biết rằng khi nào nên tạo một đối tượng có chức năng cần thiết nhưng loại đối tượng sẽ vẫn chưa được quyết định hoặc nó sẽ được quyết định tham số động ob được thông qua .
Bây giờ vấn đề là, gần như có thể đạt được bằng cách sử dụng chính mô hình nhà máy nhưng một nhược điểm lớn sẽ đưa vào hệ thống nếu mô hình nhà máy sẽ được sử dụng cho vấn đề được tô sáng ở trên, đó là logic của bạn đối với các đối tượng khác nhau (đối tượng lớp phụ) sẽ cụ thể với một số điều kiện kinh doanh vì vậy trong tương lai khi bạn cần mở rộng chức năng thư viện của mình cho các nền tảng khác (Về mặt kỹ thuật hơn, bạn cần thêm nhiều lớp phụ của giao diện cơ bản hoặc lớp trừu tượng để nhà máy cũng trả lại các đối tượng đó ngoài các đối tượng hiện có dựa trên một số tham số động) sau đó mỗi khi bạn cần thay đổi (mở rộng) logic của lớp nhà máy sẽ hoạt động tốn kém và không tốt từ quan điểm thiết kế. Mặt khác, nếu "phương pháp nhà máy"
interface Deliverable
{
/*********/
}
abstract class DefaultProducer
{
public void taskToBeDone()
{
Deliverable deliverable = factoryMethodPattern();
}
protected abstract Deliverable factoryMethodPattern();
}
class SpecificDeliverable implements Deliverable
{
/***SPECIFIC TASK CAN BE WRITTEN HERE***/
}
class SpecificProducer extends DefaultProducer
{
protected Deliverable factoryMethodPattern()
{
return new SpecificDeliverable();
}
}
public class MasterApplicationProgram
{
public static void main(String arg[])
{
DefaultProducer defaultProducer = new SpecificProducer();
defaultProducer.taskToBeDone();
}
}
Đó là ý tưởng tốt để sử dụng các phương thức nhà máy bên trong đối tượng khi:
Đó là ý tưởng tốt để sử dụng lớp nhà máy trừu tượng khi:
UML từ
Sản phẩm: Nó định nghĩa một giao diện của các đối tượng mà phương thức Factory tạo ra.
ConcreteSản phẩm : Triển khai Giao diện sản phẩm
Người tạo: Khai báo phương thức Factory
ConcreateCreator: Triển khai phương thức Factory để trả về một thể hiện của Concrete sản phẩm
Báo cáo sự cố: Tạo Factory of Games bằng cách sử dụng Factory Methods, định nghĩa giao diện trò chơi.
Đoạn mã:
import java.util.HashMap;
/* Product interface as per UML diagram */
interface Game{
/* createGame is a complex method, which executes a sequence of game steps */
public void createGame();
}
/* ConcreteProduct implementation as per UML diagram */
class Chess implements Game{
public Chess(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Chess game");
System.out.println("Opponents:2");
System.out.println("Define 64 blocks");
System.out.println("Place 16 pieces for White opponent");
System.out.println("Place 16 pieces for Black opponent");
System.out.println("Start Chess game");
System.out.println("---------------------------------------");
}
}
class Checkers implements Game{
public Checkers(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Checkers game");
System.out.println("Opponents:2 or 3 or 4 or 6");
System.out.println("For each opponent, place 10 coins");
System.out.println("Start Checkers game");
System.out.println("---------------------------------------");
}
}
class Ludo implements Game{
public Ludo(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Ludo game");
System.out.println("Opponents:2 or 3 or 4");
System.out.println("For each opponent, place 4 coins");
System.out.println("Create two dices with numbers from 1-6");
System.out.println("Start Ludo game");
System.out.println("---------------------------------------");
}
}
/* Creator interface as per UML diagram */
interface IGameFactory {
public Game getGame(String gameName);
}
/* ConcreteCreator implementation as per UML diagram */
class GameFactory implements IGameFactory {
HashMap<String,Game> games = new HashMap<String,Game>();
/*
Since Game Creation is complex process, we don't want to create game using new operator every time.
Instead we create Game only once and store it in Factory. When client request a specific game,
Game object is returned from Factory instead of creating new Game on the fly, which is time consuming
*/
public GameFactory(){
games.put(Chess.class.getName(),new Chess());
games.put(Checkers.class.getName(),new Checkers());
games.put(Ludo.class.getName(),new Ludo());
}
public Game getGame(String gameName){
return games.get(gameName);
}
}
public class NonStaticFactoryDemo{
public static void main(String args[]){
if ( args.length < 1){
System.out.println("Usage: java FactoryDemo gameName");
return;
}
GameFactory factory = new GameFactory();
Game game = factory.getGame(args[0]);
if ( game != null ){
game.createGame();
System.out.println("Game="+game.getClass().getName());
}else{
System.out.println(args[0]+ " Game does not exists in factory");
}
}
}
đầu ra:
java NonStaticFactoryDemo Chess
---------------------------------------
Create Chess game
Opponents:2
Define 64 blocks
Place 16 pieces for White opponent
Place 16 pieces for Black opponent
Start Chess game
---------------------------------------
Game=Chess
Ví dụ này cho thấy một Factory
lớp bằng cách thực hiện a FactoryMethod
.
Game
là giao diện cho tất cả các loại trò chơi. Nó định nghĩa phương thức phức tạp:createGame()
Chess, Ludo, Checkers
là các biến thể khác nhau của trò chơi, cung cấp việc thực hiện cho createGame()
public Game getGame(String gameName)
đang FactoryMethod
ở trong IGameFactory
lớp
GameFactory
tạo trước các loại trò chơi khác nhau trong hàm tạo. Nó thực hiện IGameFactory
phương pháp nhà máy.
Tên trò chơi được truyền dưới dạng đối số dòng lệnh để NotStaticFactoryDemo
getGame
trong việc GameFactory
chấp nhận một tên trò chơi và trả về Game
đối tượng tương ứng .
Nhà máy:
Tạo các đối tượng mà không để lộ logic khởi tạo cho máy khách.
Nhà máy
Xác định một giao diện để tạo một đối tượng, nhưng hãy để các lớp con quyết định lớp nào sẽ khởi tạo. Phương thức Factory cho phép khởi tạo lớp trì hoãn cho các lớp con
Trường hợp sử dụng:
Khi nào nên sử dụng: Client
không biết những lớp cụ thể nào sẽ được yêu cầu tạo trong thời gian chạy, nhưng chỉ muốn có một lớp sẽ thực hiện công việc.
getArea()
không phải là một phương pháp nhà máy ở tất cả .
Đó thực sự là một vấn đề của hương vị. Các lớp của nhà máy có thể được trừu tượng hóa / giao thoa khi cần thiết, trong khi các phương thức của nhà máy có trọng lượng nhẹ hơn (và cũng có xu hướng có thể kiểm tra được, vì chúng không có loại xác định, nhưng chúng sẽ yêu cầu một điểm đăng ký nổi tiếng, gần giống với dịch vụ định vị nhưng để định vị phương pháp nhà máy).
Các lớp nhà máy rất hữu ích khi loại đối tượng mà chúng trả về có một hàm tạo riêng, khi các lớp nhà máy khác nhau đặt các thuộc tính khác nhau trên đối tượng trả về hoặc khi một loại nhà máy cụ thể được ghép với loại bê tông trả về của nó.
WCF sử dụng các lớp ServicehostFactory để truy xuất các đối tượng Servicehost trong các tình huống khác nhau. ServicehostFactory tiêu chuẩn được IIS sử dụng để truy xuất các phiên bản Servicehost cho các tệp .svc, nhưng WebScriptServicehostFactory được sử dụng cho các dịch vụ trả về tuần tự hóa cho các máy khách JavaScript. Dịch vụ dữ liệu ADO.NET có DataServicehostFactory đặc biệt của riêng mình và ASP.NET có ApplicationServicehostFactory vì các dịch vụ của nó có các nhà xây dựng riêng.
Nếu bạn chỉ có một lớp tiêu thụ nhà máy, thì bạn chỉ có thể sử dụng một phương thức nhà máy trong lớp đó.
Hãy xem xét một kịch bản khi bạn phải thiết kế một lớp Đơn hàng và Khách hàng. Để đơn giản và các yêu cầu ban đầu, bạn không cảm thấy cần nhà máy cho lớp Đơn hàng và điền vào ứng dụng của bạn bằng nhiều câu lệnh 'Đơn hàng mới ()'. Mọi thứ đang hoạt động tốt.
Bây giờ một yêu cầu mới xuất hiện rằng đối tượng Đặt hàng không thể được khởi tạo mà không có liên kết Khách hàng (phụ thuộc mới). Bây giờ bạn có những cân nhắc sau đây.
1- Bạn tạo quá tải hàm tạo sẽ chỉ hoạt động cho các triển khai mới. (Không thể chấp nhận). 2- Bạn thay đổi chữ ký Order () và thay đổi mỗi lần gọi. (Không phải là một thực hành tốt và đau thực sự).
Thay vào đó Nếu bạn đã tạo một nhà máy cho Lớp Đặt hàng, bạn chỉ phải thay đổi một dòng mã và bạn sẽ ổn. Tôi đề nghị lớp Factory cho hầu hết các hiệp hội tổng hợp. Mong rằng sẽ giúp.
nếu bạn muốn tạo một đối tượng khác về mặt sử dụng. Nó rất hữu ích.
public class factoryMethodPattern {
static String planName = "COMMERCIALPLAN";
static int units = 3;
public static void main(String args[]) {
GetPlanFactory planFactory = new GetPlanFactory();
Plan p = planFactory.getPlan(planName);
System.out.print("Bill amount for " + planName + " of " + units
+ " units is: ");
p.getRate();
p.calculateBill(units);
}
}
abstract class Plan {
protected double rate;
abstract void getRate();
public void calculateBill(int units) {
System.out.println(units * rate);
}
}
class DomesticPlan extends Plan {
// @override
public void getRate() {
rate = 3.50;
}
}
class CommercialPlan extends Plan {
// @override
public void getRate() {
rate = 7.50;
}
}
class InstitutionalPlan extends Plan {
// @override
public void getRate() {
rate = 5.50;
}
}
class GetPlanFactory {
// use getPlan method to get object of type Plan
public Plan getPlan(String planType) {
if (planType == null) {
return null;
}
if (planType.equalsIgnoreCase("DOMESTICPLAN")) {
return new DomesticPlan();
} else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) {
return new CommercialPlan();
} else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) {
return new InstitutionalPlan();
}
return null;
}
}
Bất kỳ lớp nào trì hoãn việc tạo đối tượng cho lớp con của nó cho đối tượng mà nó cần làm việc có thể được xem như một ví dụ về mẫu Factory.
Tôi đã đề cập chi tiết trong một câu trả lời khác tại https://stackoverflow.com/a/49110001/504133
Tôi nghĩ rằng nó sẽ phụ thuộc vào mức độ khớp nối lỏng lẻo mà bạn muốn mang đến mã của mình.
Phương pháp nhà máy tách rời mọi thứ rất tốt nhưng lớp nhà máy không có.
Nói cách khác, việc thay đổi mọi thứ sẽ dễ dàng hơn nếu bạn sử dụng phương thức nhà máy hơn là khi bạn sử dụng một nhà máy đơn giản (được gọi là lớp nhà máy).
Nhìn vào ví dụ này: https://connected2ledge.com/programming/java-factory-potype/ . Bây giờ, hãy tưởng tượng rằng bạn muốn mang một Động vật mới. Trong lớp Factory, bạn cần thay đổi Factory nhưng trong phương thức Factory, không, bạn chỉ cần thêm một lớp con mới.
Các lớp học nhà máy nặng hơn, nhưng cung cấp cho bạn một số lợi thế nhất định. Trong trường hợp khi bạn cần xây dựng các đối tượng của mình từ nhiều nguồn dữ liệu thô, chúng cho phép bạn chỉ gói gọn logic xây dựng (và có thể tổng hợp dữ liệu) ở một nơi. Ở đó nó có thể được kiểm tra một cách trừu tượng mà không cần quan tâm đến giao diện đối tượng.
Tôi đã tìm thấy đây là một mẫu hữu ích, đặc biệt khi tôi không thể thay thế và không đủ ORM và muốn khởi tạo hiệu quả nhiều đối tượng từ bảng DB tham gia hoặc các thủ tục được lưu trữ.
Tôi thích các nhà máy với khái niệm về thư viện. Ví dụ, bạn có thể có một thư viện để làm việc với các số và một thư viện khác để làm việc với các hình dạng. Bạn có thể lưu trữ các chức năng của các thư viện này trong các thư mục có tên logic là Numbers
hoặc Shapes
. Đây là các loại chung có thể bao gồm số nguyên, phao, dobules, dài hoặc hình chữ nhật, hình tròn, hình tam giác, hình ngũ giác trong trường hợp hình dạng.
Petter nhà máy sử dụng đa hình, tiêm phụ thuộc và đảo ngược kiểm soát.
Mục đích đã nêu của các mẫu nhà máy là: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Vì vậy, giả sử rằng bạn đang xây dựng một Hệ điều hành hoặc Khung và bạn đang xây dựng tất cả các thành phần riêng biệt.
Dưới đây là một ví dụ đơn giản về khái niệm Mô hình nhà máy trong PHP. Tôi có thể không 100% cho tất cả nhưng nó dự định là một ví dụ đơn giản. Tôi không phải là một chuyên gia.
class NumbersFactory {
public static function makeNumber( $type, $number ) {
$numObject = null;
$number = null;
switch( $type ) {
case 'float':
$numObject = new Float( $number );
break;
case 'integer':
$numObject = new Integer( $number );
break;
case 'short':
$numObject = new Short( $number );
break;
case 'double':
$numObject = new Double( $number );
break;
case 'long':
$numObject = new Long( $number );
break;
default:
$numObject = new Integer( $number );
break;
}
return $numObject;
}
}
/* Numbers interface */
abstract class Number {
protected $number;
public function __construct( $number ) {
$this->number = $number;
}
abstract public function add();
abstract public function subtract();
abstract public function multiply();
abstract public function divide();
}
/* Float Implementation */
class Float extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Integer Implementation */
class Integer extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Short Implementation */
class Short extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Double Implementation */
class Double extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Long Implementation */
class Long extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
$number = NumbersFactory::makeNumber( 'float', 12.5 );
NumbersFactory::makeNumber( 'float', 12.5 );
cho tôi hơn chỉ nói new Float(12.5);
nếu tôi biết tôi cần một Float
? Đây là những gì tôi không hiểu về các nhà máy ... vấn đề là gì?