工厂方法(学习笔记)

  1. 意图

  定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。

 

  2. 别名

  虚构造器(virtual constructor)

 

  3. 动机(参考Design pattern)

  框架使用抽象类定义和维护对象之间的关系。这些对象关系的创建也通常由框架负责。

  考虑一个向用户显示多个文档的应用框架。在这个框架中,两个主要的抽象类Application和Document。客户通过重定义其子类实现具体应用。例如,定义一个绘图应用,定义类DrawingApplication和DrawingDocument。Application类负责管理Document类并根据需求创建它们——例如,当用户从菜单选择Open或New的时候。

  因为被实例化的特定Document子类是与特定应用相关的,所以Application类不可能预测到哪个Document子类被实例化。

  Factory Method模式封装了哪个Document子类将被创建的信息并将这些信息从框架中分离出来,如下图所示。

 

 

   Application 的子类重定义Application的抽象操作CreateDocument已返回适当的Document子类对象。Application子类实例化后,其可以实例化与文档相关的类而无须知道这些文档的类。称CreateDocument是一个工厂方法,它负责生产一个对象。

 

  4. 适用性

  • 当一个类不知道它所必须创建的对象的类的时候
  • 当一个类希望由它的子类来制定他所创建的对象的时候
  • 复用现有对象来节省系统资源,而不是每次都重新创建对象

  在处理大型资源密集型对象(比如数据库连接、文件系统和网络资源)时,经常会碰到这种资源需求

  复用现有对象的方法:

  1. 创建存储空间存放所有已创建的对象

  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()
posted @ 2021-07-26 22:15  慕仙白  阅读(140)  评论(0编辑  收藏  举报