it-swarm-vi.tech

Mô hình nhà máy. Khi nào sử dụng phương pháp nhà máy?

Khi nào nên sử dụng các phương thức Factory trong một đối tượng thay vì lớp Factory?

249
jjshell

Tôi thích suy nghĩ về các thiết kế vỗ về các lớp học 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ê 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 bất kỳ điều gì - 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ể có đượ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.

355
kyoryu

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, v.v.

84
Rasmus Faber

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 đối tượng House, lần lượt có một đối tượng KitchenLivingRoom và đối tượng LivingRoom cũng có một đối tượng TV

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 House của bạn 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.

Thay thế 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 House không thành công, chỉ có một nơi để tìm, nhưng việc phải sử dụng đoạn dữ liệu này mỗi khi người ta muốn một House mới 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 House được trừu tượng hóa (trong đó bạn không cần tạo và thiết lập mọi phụ thuộc duy nhất khi bạn chỉ muốn tạo House) và đồng thời tập trung hóa giúp việc bảo 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 tốt nhất cách các lớp Factory có thể hữu ích.

66
Mahn

Đ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 khác nhau 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ẽ không đượ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à, có thể đạt được mức độ tương tự bằng cách sử dụng chính mẫu nhà máy nhưng một nhược điểm lớn sẽ đưa vào hệ thống nếu mẫu nhà máy sẽ được sử dụng cho vấn đề được tô sáng ở trên, đó là logic của bạn về các đối tượng khác nhau (đối tượng lớp phụ) cụ thể với một số điều kiện kinh doanh để 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 lớp 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 sử dụng mẫu "phương thức nhà máy" để thực hiện điều tương tự, sau đó bạn chỉ cần tạo chức năng bổ sung (các lớp con) và đăng ký nó một cách linh hoạt bằng cách tiêm mà không yêu cầu thay đổi trong mã cơ sở của bạn.

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();
    }
}
16
Prakash Chhipa

Đó là ý tưởng tốt để sử dụng phương thức nhà máy bên trong đối tượng khi:

  1. Lớp đối tượng không biết lớp con chính xác mà nó phải tạo
  2. Lớp đối tượng được thiết kế sao cho các đối tượng mà nó tạo ra được chỉ định bởi các lớp con
  3. Lớp đối tượng ủy thác nhiệm vụ của mình cho các lớp phụ trợ và không biết lớp chính xác nào sẽ nhận những nhiệm vụ này

Đó là ý tưởng tốt để sử dụng nhà máy trừu tượng class khi:

  1. Đối tượng của bạn không nên phụ thuộc vào cách các đối tượng bên trong của nó được tạo và thiết kế
  2. Nhóm các đối tượng được liên kết nên được sử dụng cùng nhau và bạn cần phục vụ ràng buộc này
  3. Đối tượng nên được cấu hình bởi một trong một số họ có thể của các đối tượng được liên kết sẽ là một phần của đối tượng cha của bạn
  4. Cần chia sẻ các đối tượng con chỉ hiển thị giao diện nhưng không phải là triển khai
14
Dzianis Yafimau

Chúng cũng hữu ích khi bạn cần một số "hàm tạo" có cùng loại tham số nhưng với hành vi khác nhau. 

14
Rik

UML từ 

 enter image description here

Sản phẩm: Nó xác định giao diện của các đối tượng mà phương thức Factory tạo.

ConcreteSản phẩm: Triển khai Giao diện sản phẩm

Creator: Khai báo phương thức Factory

ConcreateCreator: Thực hiện phương thức Factory để trả về một thể hiện của Concrete Productst

Tuyên bố vấn đề: 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 lớp Factory bằng cách triển khai một FactoryMethod.

  1. 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()

  2. Chess, Ludo, Checkers là các biến thể trò chơi khác nhau, cung cấp triển khai cho createGame()

  3. public Game getGame(String gameName)FactoryMethod trong lớp IGameFactory

  4. 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 phương thức nhà máy IGameFactory

  5. tên trò chơi được truyền dưới dạng đối số dòng lệnh cho NotStaticFactoryDemo

  6. getGame in GameFactory chấp nhận tên trò chơi và trả về đối tượng Game 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.

FactoryMethod

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 khi chạy, nhưng chỉ muốn có một lớp sẽ thực hiện công việc.

9
Ravindra babu

Đó 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).

5
Brad Wilson

Các lớp nhà máy rất hữu ích khi loại đối tượng mà chúng trả về có 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ó. 

WCFsử 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ả lại 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 đó.

4
Mark Cidade

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 trong hình ảnh 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 được chấp nhận) . 2- Bạn thay đổi chữ ký của Order () và thay đổi từng điều khoản. (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.

3
Muhammad Awais

Theo trang web sourcemaking, ý định của nó là:

  • 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.

  • Xác định một hàm tạo "ảo".

  • Các nhà điều hành mới coi có hại.

Một ví dụ về cách nó có thể được sử dụng:

abstract class AbstractFactoryMethod {
    abstract function makePHPBook($param);
}

class OReillyFactoryMethod extends AbstractFactoryMethod
{
    function makePHPBook($param)
    {
        $book = NULL;  
        switch ($param) {
            case "us":
                $book = new OReillyPHPBook();
            break;
            // Other classes...
            case "other":
                $book = new SamsPHPBook();
            break;
            default:
                $book = new OReillyPHPBook();
            break;        
    }

    return $book;
}

Và sau đó là thử nghiệm:

function testFactoryMethod($factoryMethodInstance)
{
    $phpUs = $factoryMethodInstance->makePHPBook("us");
    echo 'us php Author: '.$phpUs->getAuthor();
    echo 'us php Title: '.$phpUs->getTitle();
}

echo 'Testing OReillyFactoryMethod';
$factoryMethodInstance = new OReillyFactoryMethod();
testFactoryMethod($factoryMethodInstance);
3
Alexander Beat

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

1
nits.kk

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 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 );
0
Robert Rocha

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ữ.

0
jonfm

Ví dụ trừu tượng.

    TypeImpl<String> type = new TypeImpl<>();
    type.addType("Condition");
    type.addType("Hazardous");

    AbstractTypeFactory<String, Tag> tags = new AbstractTypeFactory<String, Tag>(type) {

        @Override
        public Tag create(String string) {
            String tp = type.find(string);

            switch (tp) {
                case "Hazardous":
                    return new HazardousTag();
                case "Condition":
                    return new ConditionTag();
                default:
                    return null;
            }
        }
    };

    Tag tagHazardous = tags.create("Hazardous");
    Tag tagCondition = tags.create("Condition");

}
0
Vahe Gharibyan

nếu bạn muốn tạo một đối tượng khác nhau 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;
      }
}
0
Samet öztoprak