工廠方法(學習筆記)

  1. 意圖

  定義一個用於建立對象的接口,讓子類決定實例化哪個類。Factory Method使一個類的實例化延遲到其子類。java

 

  2. 別名

  虛構造器(virtual constructor)數據庫

 

  3. 動機(參考Design pattern)

  框架使用抽象類定義和維護對象之間的關係。這些對象關係的建立也一般由框架負責。網絡

  考慮一個向用戶顯示多個文檔的應用框架。在這個框架中,兩個主要的抽象類Application和Document。客戶經過重定義其子類實現具體應用。例如,定義一個繪圖應用,定義類DrawingApplication和DrawingDocument。Application類負責管理Document類並根據需求建立它們——例如,當用戶從菜單選擇Open或New的時候。框架

  由於被實例化的特定Document子類是與特定應用相關的,因此Application類不可能預測到哪一個Document子類被實例化。ide

  Factory Method模式封裝了哪一個Document子類將被建立的信息並將這些信息從框架中分離出來,以下圖所示。ui

 

 

   Application 的子類重定義Application的抽象操做CreateDocument已返回適當的Document子類對象。Application子類實例化後,其能夠實例化與文檔相關的類而無須知道這些文檔的類。稱CreateDocument是一個工廠方法,它負責生產一個對象。this

 

  4. 適用性

  • 當一個類不知道它所必須建立的對象的類的時候
  • 當一個類但願由它的子類來制定他所建立的對象的時候
  • 複用現有對象來節省系統資源,而不是每次都從新建立對象

  在處理大型資源密集型對象(好比數據庫鏈接、文件系統和網絡資源)時,常常會碰到這種資源需求spa

  複用現有對象的方法:3d

  1. 建立存儲空間存放全部已建立的對象code

  2. 當他人請求一個對象時,程序將在對象池中搜索可用對象

  3. 而後將其返回給客戶端代碼

  4. 若是沒有可用對象,就建立一個新對象,並添加到對象池中

  5. 結構

 

  6. 效果

  1) 能夠避免建立者和具體產品之間的緊密耦合

  2) 單一職責原則。 能夠將產品建立代碼放在程序的單一位置, 從而使得代碼更容易維護

  3) 開閉原則。 無需更改現有客戶端代碼, 你就能夠在程序中引入新的產品類型

  7. 代碼實現

  buttons

  buttons/Button.java: 通用產品接口  

package factory_method.buttons;

/**
 * @author GaoMing
 * @date 2021/7/18 - 11:43
 * Common interface for all buttons.
 */
public interface Button {
    void render();
    void onClick();
}

  buttons/HtmlButton.java: 具體產品

package factory_method.buttons;

/**
 * @author GaoMing
 * @date 2021/7/18 - 11:43
 * HTML button implementation.
 */
public class HtmlButton implements Button{
    public void render() {
        System.out.println("<button>Test Button</button>");
        onClick();
    }

    public void onClick() {
        System.out.println("Click! Button says - 'Hello World!'");
    }
}

  buttons/WindowsButton.java: 另外一個具體產品

package factory_method.buttons;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * @author GaoMing
 * @date 2021/7/18 - 11:45
 * Windows button implementation.
 */
public class WindowsButton implements Button{
    JPanel panel = new JPanel();
    JFrame frame = new JFrame();
    JButton button;

    public void render(){
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JLabel label = new JLabel("Hello World!");
        label.setOpaque(true);
        label.setBackground(new Color(235,233,126));
        label.setFont(new Font("Dialog", Font.BOLD, 44));
        label.setHorizontalAlignment(SwingConstants.CENTER);
        panel.setLayout(new FlowLayout(FlowLayout.CENTER));
        frame.getContentPane().add(panel);
        panel.add(label);
        onClick();
        panel.add(button);

        frame.setSize(320, 200);
        frame.setVisible(true);
        onClick();
    }
    public void onClick() {
        button = new JButton("Exit");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                frame.setVisible(false);
                System.exit(0);
            }
        });
    }

}

  factories

  factories/Dialog.java: 基礎建立者

package factory_method.factories;
import factory_method.buttons.Button;

/**
 * @author GaoMing
 * @date 2021/7/18 - 11:50
 * Base factory class. Note that "factory" is merely a role for the class. It
 * should have some core business logic which needs different products to be
 * created.
 */
public abstract class Dialog {
    public void renderWindow() {
        // ... other code ...

        Button okButton = createButton();
        okButton.render();
    }

    /**
     * Subclasses will override this method in order to create specific button
     * objects.
     */
    public abstract Button createButton();
}

  factories/HtmlDialog.java: 具體建立者

package factory_method.factories;

import factory_method.buttons.HtmlButton;
import factory_method.buttons.Button;

/**
 * @author GaoMing
 * @date 2021/7/18 - 11:54
 */
public class HtmlDialog extends Dialog{
    @Override
    public Button createButton() {
        return new HtmlButton();
    }
}

  factories/WindowsDialog.java: 另外一個具體建立者

package factory_method.factories;

import factory_method.buttons.WindowsButton;
import factory_method.buttons.Button;
/**
 * @author GaoMing
 * @date 2021/7/18 - 11:54
 */
public class WindowsDialog extends Dialog{
    @Override
    public Button createButton() {
        return new WindowsButton();
    }
}

  Demo.java: 客戶端代碼

package factory_method;

import factory_method.factories.HtmlDialog;
import factory_method.factories.WindowsDialog;
import factory_method.factories.Dialog;

/**
 * @author GaoMing
 * @date 2021/7/18 - 11:56
 * Demo class. Everything comes together here.
 */
    public class Demo {
        private static Dialog dialog;

        public static void main(String[] args) {
            configure();
            runBusinessLogic();
        }

        /**
         * The concrete factory is usually chosen depending on configuration or
         * environment options.
         */
        static void configure() {
            if (System.getProperty("os.name").equals("Windows 10")) {
                dialog = new WindowsDialog();
            } else {
                dialog = new HtmlDialog();
            }
        }

        /**
         * All of the client code should work with factories and products through
         * abstract interfaces. This way it does not care which factory it works
         * with and what kind of product it returns.
         */
        static void runBusinessLogic() {
            dialog.renderWindow();
        }
    }

  OutputDemo.png: 執行結果 (Windows­Dialog)

 

  8. 與其餘模式的關係

  • 能夠同時使用工廠方法和迭代器模式來讓子類集合返回不一樣類型的迭代器, 並使得迭代器與集合相匹配
  • 原型並不基於繼承, 所以沒有繼承的缺點。 另外一方面, 原型須要對被複制對象進行復雜的初始化。 工廠方法基於繼承, 可是它不須要初始化步驟
  • 工廠方法是模板方法模式的一種特殊形式。 同時, 工廠方法能夠做爲一個大型模板方法中的一個步驟

  9. 已知應用 

  • javax.xml.parsers.DocumentBuilderFactory#newInstance()
  • javax.xml.transform.TransformerFactory#newInstance()
  • javax.xml.xpath.XPathFactory#newInstance()
相關文章
相關標籤/搜索