1.定义
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
简单来说:生成器模式就是将一个复杂对象的构建与它的使用分离,使用同样的构建过程可以创建不同的表示。
2.概述
如果一个类中有若干个成员变量是其他类声明的对象,那么该类创建的对象就可以包含若干个其他对象作为其成员。习惯上把一个对象中的成员对象称作为它中的组件。
例如,Geometry类中含有Triangle、Rectangle和Circle类声明的对象,那么Geometry类就可以创建一个由三角形、矩形和圆形成的几何图形,三角形,矩形和圆形就是当前几何图形中的组件。
但是,在编写Geometry类的构造方法的代码时可能遇到如下的问题:
1.有些用户并不需要Geometry类所创建的对象含有其全部组件,即不希望当前图形实例化它含有的全部组件,如果在Geometry类的构造方法中创建了Triangle、Rectangle和Circle声明的对象就无法满足这些用户的需求。
2.有些用户对所创建的Geometry对象中的组件是由特殊的要求,比如某个用户要求Geometry对象中的三角形是等边的,矩形是正方的;某个用户要求Geometry对象中三角形必须是直角的,等等。
显然,如果一个对象由很多组件构造,我们无法在构造方法中进行硬编码来满足各种用户对组件结构的要求。
因此,按着面向抽象的原则,我们不应该在Geometry类的构造方法中进行任何的编码,而是将Geometry对象的构造过程分成若干个步骤,即根据当前组件的个数,在一个接口中定义若干个方法,每个方法负责创建Geometry对象的一个组件,而实现该接口的类将负责创建Geometry对象,也就是说,将Geometry对象的创建过程封装在另一个类中。
3.应用场景
1、需要生成的对象具有复杂的内部结构。
2、需要生成的对象内部属性本身相互依赖。
4.模式的结构与使用
生成器模式的结构中包括四种角色。
1.产品(product):具体生成器要构造的复杂对象;
2.抽象生成器(Builder):抽象生成器是一个接口,该接口除了为创建一个Product对象的各个组件定义了若干个方法外,还要定义返回Product对象的方法;
3. 具体生成器(ConcreteBuilder):实现了Builder接口的类,具体生成器将实现Builder接口所定义的方法。;
4. 指挥者(Director):指挥者是一个类,该类需含有Builder接口声明的变量。指挥者的职责是负责向用户提供具体生成器,即指挥者将请求具体生成器来构造用户所需要的Product对象,如果所请求的具体对象生成器成功德构造出Product对象,指挥者就可以让该具体生成器返回所构造的Product对象。
1.生成器模式的UML类图
2.结构的描述
以下通过一个简单的问题来描述生成器模式所涉及的各个角色。
这个简单的问题就是创建含有按钮,标签和文本框组件的容器。不同用户对容器有不同的要求,比如某些用户希望容器中只含有按钮和标签(不含文本框),某些用户希望容器只含有按钮和文本框(不含标签)等。另外,用户对组件在容器中的顺序位置也有不同的要求,比如某些用户要求组件在容积中从左到右的排列顺序是按钮,标签,文本框,而某些用户要求从左到右的排列顺序是标签,文本框,按钮等。
显然不能再容器的构造方法中编写有关创建按钮,标签和文本框的代码,也不能编写排列这些组件位置的代码。以下使用生成器模式为用户创建所需要的容器,具体如下所示:
1.产品(Product)
产品角色是PanelProduct类,该类的代码如下:
在该类定义了三个组件,包括按钮,标签以及文本框,在该类没有定义构造函数。
import javax.swing.*;
public class PanelProduct extends JPanel{
JButton button;
JLabel label;
JTextField textField;
}
2.抽象生成器(Builder)
抽象生成器是Builder接口,其代码如下所示:
在里面分别定义了实例化组件的方法,以及得到一个由各组件组合生成的JPanel 方法。
import javax.swing.*;
public interface Builder {
public abstract void buildButton();
public abstract void buildLabel();
public abstract void buildTextField();
public abstract JPanel getPanel();
}
3.具体生成器(Concretebuilder)
这里主要有两个生成器ConcreteBuilderOne与ConcreteBuilderTwo类,代码如下所示:
在里面实现了Builder接口,重写了Builder的抽象方法,以及得到一个具体拥有自己个性化组件顺序的getPanel()方法。
ConcreteBuilderOne.java
import javax.swing.*;
public class ConcreteBuilderOne implements Builder{
private PanelProduct panel;
public ConcreteBuilderOne() {
this.panel = new PanelProduct();
}
@Override
public void buildButton() {
panel.button=new JButton("按钮");
}
@Override
public void buildLabel() {
panel.label=new JLabel("标签");
}
@Override
public void buildTextField() {
panel.textField=new JTextField("文本框");
}
@Override
public JPanel getPanel() {
panel.add(panel.button);//与ConcreteBuilderTwo添加组件的顺序不同
panel.add(panel.label);
panel.add(panel.textField);
return panel;
}
}
ConcreteBuilderTwo.java
import javax.swing.*;
public class ConcreteBuilderTwo implements Builder{
private PanelProduct panel;//容器
public ConcreteBuilderTwo() {
this.panel = new PanelProduct();
}
@Override
public void buildButton() {
panel.button=new JButton("button");
}
@Override
public void buildLabel() {
panel.label=new JLabel("label");
}
@Override
public void buildTextField() {
panel.textField=new JTextField("textLabel");
}
@Override
public JPanel getPanel() {
panel.add(panel.textField);
panel.add(panel.label);
panel.add(panel.button);
return panel;
}
}
4.指挥者
指挥者是Director类,代码如下所示:
在该类中引用了Builder变量,通过该抽象变量的getPanel()方法得到具体的具有个性化的组件组合的排列顺序。
import javax.swing.*;
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public JPanel constructProduct(){
builder.buildButton();
builder.buildLabel();
builder.buildTextField();
JPanel product = builder.getPanel();
return product;
}
}
5.测试程序
import javax.swing.*;
public class Application {
public static void main(String[] args) {
Builder builder=new ConcreteBuilderOne();
Director director = new Director(builder);
JPanel panel = director.constructProduct();
JFrame frameOne=new JFrame();
frameOne.add(panel);
frameOne.setBounds(12,12,200,120);
frameOne.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frameOne.setVisible(true);
builder=new ConcreteBuilderTwo();
director=new Director(builder);
panel=director.constructProduct();
JFrame jFrameTwo=new JFrame();
jFrameTwo.add(panel);
jFrameTwo.setBounds(212,12,200,120);
jFrameTwo.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jFrameTwo.setVisible(true);
}
}
6.结果展示
5.生成器模式的优点
1.生成器模式将对象的构建过程封装在具体生成器中,用户使用不同的具体生成器就可以得到该对象的不同表示。
2.生成器模式将对象的构建过程从创建该对象的类中分离出来,使的用户无须了解该对象的具体组件内。
3.可以更加精细有效地控制对象的构造过程。生成器将对象的构造过程分解到若干个步骤,这就使程序可以更加精细,有效地控制整个对象的构造。
4.生成器模式将对象的构造过程与创建该对象类解耦,使对象的创建更加灵活有弹性。
5.当增加新的具体生成器时,不必修改指挥者的代码,即该模式满足开闭原则。