工厂方法模式

105 阅读3分钟

Factory Method Pattern.

别名:虚拟构造器( Virtual Constructor)


定义

定义一个创建对象的接口,让子类决定实例化哪一个类,将类的实例化推迟到子类中进行。


应用场景

1.无法预知确切的类型及其依赖关系

工厂方法将创建产品的代码与实际使用产品的代码分离,则能在不影响其他代码的情况下扩展产品的创建部分代码。「 eg: 如果需要向应用添加一种新产品,则只需创建新的创建者子类,并重写该工厂方法即可 」

2.扩展 软件库 或 框架 的内部组件

3.通过复用现有对象来节省系统资源


实现方法

1.让所有产品都遵循同一接口。该接口含有对所有产品都有意义的方法

2.在创建类中添加一个空的工厂方法。该方法的返回类型和遵循通用的产品接口保持一致

3.为工厂方法中的每个产品编写一个建造者子类,并且在子类重写工厂方法。将相关创建的代码放入工厂方法中

4.当产品类型过多时,便没必要每个都创建创建者子类,而可以复用基类的控制参数

5.当基础工厂方法中已经没有任何代码,则可以转变为抽象类。若还有其它语句,则可以设置为该方法的默认行为


优缺点

优点:

1.避免创建者和具体产品的紧密耦合

2.单一职责原则。将产品的创建代码放在程序的单一位置上,从而使代码更易维护

3.开闭原则。增加新产品时,不需改变原有代码,只需引入新的产品及其创建者

缺点:

1.需要大量的子类,会使代码变得复杂。【最好的情况是将该模式引入创建者类的现有层次结构中】

结构

UML图

classDiagram
Creator<|--ConcreteCreatorA
Creator<|--ConcreteCreatorB
Product<..Creator
Product<|..ConcreteProductA
Product<|..ConcreteProductB
class Creator{
	....
	+someOperation()
	+createProduct()  Product
}

参与者

  1. 产品 ( Product ) : 对接口进行声明【由建造者及其子类构建的对象】

  2. 具体产品 ( Concrete Products ) :是产品接口的不同实现

  3. 创建者 ( Creator ) :类声明 返回产品对象的工厂方法。该方法的返回类型必须与产品接口相匹配。【主要职责不是创建产品。通常包含一些与产品相关的核心业务逻辑。主要是将这些逻辑从具体产品中分离出来】

  4. 具体创建者 ( Concrete Creators ) :重写声明的基础方法,使其返回不同类型的产品。【不仅可以是创建新的实例,还可以是返回缓存、对象池或其他来源的已有对象】

通用写法

public abstract class Product {
   void show(){};
}
​
public class ConcreteProductA extends Product{
   @Override
   void show() {
       System.out.println("this is ProductA");
  }
}
​
public class ConcreteProductB extends Product{
   @Override
   void show() {
       System.out.println("this is ProductB");
  }
}
​
public interface Creator {
   Product createProduct();
}
​
public class ConcreteCreatorA implements Creator{
   @Override
   public Product createProduct() {
       return new ConcreteProductA();
  }
}
​
public class ConcreteCreatorB implements Creator{
   @Override
   public Product createProduct() {
       return new ConcreteProductB();
  }
}
​
public class Client {
   public static void main(String[] args) {
       Creator creator = new ConcreteCreatorA();
       creator.createProduct().show();
​
       creator = new ConcreteCreatorB();
       creator.createProduct().show();
  }
}

案例

1.生成跨平台的 GUI 元素

不同类型的对话框需要其各自类型的元素。因此我们需要对每个对话框类型创建子类并重写其工厂方法

按钮类

public interface Button {
   void render();
   void onClick();
}
​
public class HtmlButton implements Button{
   @Override
   public void render() {
       System.out.println("<button>HTML Button</button>");
  }
​
   @Override
   public void onClick() {
       System.out.println("Click ==> 'HEELO WORLD'");
  }
}
​
public class WindowButton implements Button{
   JPanel panel = new JPanel();
   JFrame frame = new JFrame();
   JButton button;
​
   @Override
   public void render() {
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       JLabel label = new JLabel("HELLO");
       label.setOpaque(true);
       label.setBackground(new Color(250,250,0));
       label.setHorizontalAlignment(SwingConstants.CENTER);
       label.setLayout(new FlowLayout(FlowLayout.CENTER));
       frame.getContentPane().add(panel);
       panel.add(label);
       onClick();
       panel.add(button);
​
       frame.setSize(320,200);
       frame.setVisible(true);
       onClick();
  }
​
   @Override
   public void onClick() {
       button = new JButton("Exit");
       button.addActionListener(new ActionListener() {
           public void actionPerformed(ActionEvent e) {
               frame.setVisible(false);
               System.exit(0);
          }
      });
  }
}

创建者类

public abstract class Dialog {
   public void renderWindow(){
       Button okButton = createButton();
       okButton.render();
  }
​
   public abstract Button createButton();
}
​
public class HtmlDialog extends Dialog{
   @Override
   public Button createButton() {
       return new HtmlButton();
  }
}
​
public class WindowDialog extends Dialog{
​
   @Override
   public Button createButton() {
       return new WindowButton();
  }
}

Client

public class Client {
   private static Dialog dialog;
​
   public static void main(String[] args) {
       configure();
       runBusinessLogic();
  }
​
   static void configure(){
       String type = "window";
       if (type.equals("window")){
           dialog = new WindowDialog();
      }else {
           dialog = new HtmlDialog();
      }
​
​
  }
​
   static void runBusinessLogic(){
       dialog.renderWindow();
  }
}