建造者模式(Builder Pattern)也叫做生成器模式,今天让我们一起学习一下建造者模式。
一、基本介绍
建造者模式的定义为:
将一个复杂对象的构建和它的表示分离开,使得同样的构建过程可以创建不同的表示。
建造者模式主要由4个角色来组成:
1 . 抽象建造者(Builder)角色:该角色用于规范产品的各个组成部分,并进行抽象,一般独立于应用程序的逻辑。
2 . 具体建造者(Concrete Builder)角色:该角色实现抽象建造者中定义的所有方法,并且返回一个组建好的产品实例。
3 . 产品(Product)角色:该角色是建造者中的复杂对象,一个系统中会有多于一个的产品类,这些产品类并不一定有共同的接口,完全可以是不相关联的。
4 . 导演者(Director)角色:该角色负责安排已有模块的顺序,然后告诉Builder开始建造。
二、代码实现建造者模式
上面说的东西都只是理论,多少有些空洞,现在我们就通过一个简单的例子来体验一下建造者模式。
1 . 创建产品类Product.java
类
/**
* 产品类
*
*/
public class Product {
//业务处理方法
}
由于我们的目的是最终生产产品,所以产品类中的逻辑实现我们暂且不关注,具体的方法要根据业务来写。
2 . 创建抽象建造者类Builder.java
类
/**
* 抽象建造者类
*
*/
public abstract class Builder {
//设置产品的不同部分,以获得不同的产品
public abstract void setPart1();
public abstract void setPart2();
public abstract void setPart3();
//建造产品
public abstract Product builderProduct();
}
该类是一个抽象类,其中我们声明了4个抽象方法,前面三个是负责给产品添加不同的部件,第四个方法是负责建造产品。但这只是一个框架,还没有具体的实现。
3 . 创建具体建造者类ConcreteBuilder.java
类
/**
*具体建造者类
*
*/
public class ConcreteBuilder extends Builder{
//一个产品
private Product product = new Product();
//开始安装产品的部件
@Override
public void setPart1() {
//为product安装部件1
}
@Override
public void setPart2() {
//为product安装部件2
}
@Override
public void setPart3() {
//为product安装部件3
}
//建造一个产品
@Override
public Product builderProduct() {
// TODO Auto-generated method stub
return product;
}
}
该类会继承自抽象建造者类
Builder
,并实现其中的方法。开始先声明一个产品,然后在各个setPart3
方法中添加具体的逻辑,然后在builderProduct()
方法中返回生产好的产品。
4 . 创建导演类Director()
类
上面我们已经实现了具体的建造者类,也具体写好了安装每个部件的方法,最后一步就是由导演类来知道具体构建者类如何制造产品啦。制造完的产品会交给导演类负责处理。
/**
* 导演类
*
*/
public class Director1 {
private Builder builder = new ConcreteBuilder();
//构建产品,调用各个添加部件的方法
public Product build(){
builder.setPart1();
builder.setPart2();
builder.setPart3();
//调用构建产品的方法来构建产品
return builder.builderProduct();
}
}
这个类很简单,其实就是获得一个具体的建造者对象,然后调用具体的方法给产品安装部件,安装完成后调用
builder.builderProduct()
方法获得建造好的产品,此处导演类是在build()
方法中完成了建造过程,同时将获得的建造好的产品返回出去,以供其他模块使用该产品。
此处的导演类起到了封装左右,可以避免高层模块深入到建造者内部的实现类,而且导演类可以有多个,根据业务逻辑分别用来建造不同的产品并输出。
三、建造者模式的优点
建造者模式的有点主要有以下几点:
1 . 封装性
。使用建造者模式可以使客户端不必知道产品的内部实现细节
2 . 独立易扩展
。由于建造过程是独立的,更利于后期扩展
3 . 便于控制细节风险
。由于具体的产品建造是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响
四、建造者模式的使用场景
说了这么多建造者模式的好处,那我们应该在什么场合使用它们呢,看下面:
1 . 相同的方法,不同的执行顺序,产生不同的结果。这种情况我们只要利用不同的导演类来控制建造过程,就可以产生不同的产品,其他部分不用修改
2 . 多个零件和部件,都可以装配到一个对象中,装配不同的零件,产生不同的运行结果,我们同样可以通过修改导演类来产生不同的产品
3 . 产品类非常复杂,此时我们也可以将产品的建造方法和具体的建造顺序分离开来处理
4 . 在对象创建过程中会用到系统的一些其他对象,这些对象在产品对象的创建过程中不容易得到,可以采用建造者模式封装该对象的创建过程
注:建造者模式关注的是零件的类型和装配工艺的顺序
五、建造者模式实站
说了半天建造产品,没行动有卵用,来来来,咋们就用刚学的
建造者模式生产两台不同类型的电脑
练练手,代码敲起来
1 . 创建产品父类Computer
类
该类是我们建造的计算机的父类,其中包含了计算机的公共属性以及属性的get和set方法
package cn.codekong.start;
/**
* 计算机类
*/
public class Computer {
//型号
private String type;
//CPU
private String cpu;
//内存
private String ram;
//硬盘
private String hardDisk;
//显示器
private String monitor;
//操作系统
private String os;
//对应的get和set方法
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getRam() {
return ram;
}
public void setRam(String ram) {
this.ram = ram;
}
public String getHardDisk() {
return hardDisk;
}
public void setHardDisk(String hardDisk) {
this.hardDisk = hardDisk;
}
public String getMonitor() {
return monitor;
}
public void setMonitor(String monitor) {
this.monitor = monitor;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
}
2 . 创建具体的产品类T410
类和X201
类
这两个类均继承自上面的
Computer
类,并且我们在该类在添加了两台计算机特有的属性,T410
计算机用于独立显卡,而X201
没有,同时重写了它们的toString()
方法,返回它们的参数,便于最后我们建造完计算机后输出它们各自的配置参数.
T410.java
package cn.codekong.start;
public class T410 extends Computer{
//显卡
private String graphicCard;
public T410() {
this.setType("Thinkpad T410");
}
public String getGraphicCard(){
return graphicCard;
}
public void setGraphicCard(String graphicCard){
this.graphicCard = graphicCard;
}
@Override
public String toString() {
return "型号:\t" + this.getType() + "\nCPU\t" + this.getCpu()
+ "\n内存\t" + this.getRam() + "\n硬盘\t" + this.getHardDisk()
+ "\n显卡\t" + this.getGraphicCard() + "\n显示器\t" + this.getMonitor()
+ "\n操作系统\t" + this.getOs();
}
}
X201.java
package cn.codekong.start;
public class X201 extends Computer{
public X201() {
this.setType("Thinkpad X201");
}
@Override
public String toString() {
return "型号:\t" + this.getType() + "\nCPU\t" + this.getCpu()
+ "\n内存\t" + this.getRam() + "\n硬盘\t" + this.getHardDisk()
+ "\n显示器\t" + this.getMonitor() + "\n操作系统\t" + this.getOs();
}
}
上面的(1)(2)步只是相当于我们有了我们需要的产品类已经声明好了,下面开始写我们的抽象建造类
3 . 抽象计算机建造类ComputerBuilder
类
我们创建了一个接口,在其中声明了我们要建造产品过程中需要用到的方法,其实就是产品的各个建造步骤
package cn.codekong.start;
/**
* 抽象的计算机建造者
* 声明建造的公共方法
*/
public interface ComputerBuilder {
//建造CPU
void buildCpu();
//建造内存
void buildRam();
//建造硬盘
void buildHardDisk();
//建造显卡
void buildGraphicCard();
//建造显示器
void buildMonitor();
//建造操作系统
void buildOs();
//得到建造好的计算机
Computer getResult();
}
4 . 创建具体的建造类T410Builder
类和X201Builder
类
这两个类要实现上一步定义的接口,然后实现里面的各个方法,其实就是实现各个具体的组装方法中的逻辑,但此时只是把每一个组装的步骤的逻辑具体化了,还没有开始正式组装。
T410Builder.java
package cn.codekong.start;
/**
* T410的具体建造者实现抽象的计算机建造者
*/
public class T410Builder implements ComputerBuilder{
private T410 computer = new T410();
@Override
public void buildCpu() {
computer.setCpu("i5-450");
}
@Override
public void buildRam() {
computer.setRam("4G 1333MHz");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("500G 7200转");
}
@Override
public void buildGraphicCard() {
computer.setGraphicCard("Nvidia");
}
@Override
public void buildMonitor() {
computer.setMonitor("14英寸 1280*800");
}
@Override
public void buildOs() {
computer.setOs("Windows7 旗舰版");
}
@Override
public Computer getResult() {
return computer;
}
}
X201Builder.java
package cn.codekong.start;
/**
* X201计算机的具体建造类实现抽象的计算机建造类
*/
public class X201Builder implements ComputerBuilder{
private X201 computer = new X201();
@Override
public void buildCpu() {
computer.setCpu("i3-350");
}
@Override
public void buildRam() {
computer.setRam("2G 1333M");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("250G 5400 转");
}
@Override
public void buildGraphicCard() {
//无独立显卡
}
@Override
public void buildMonitor() {
computer.setMonitor("12英寸 1280*800");
}
@Override
public void buildOs() {
computer.setOs("Windows7 Home版");
}
@Override
public Computer getResult() {
return computer;
}
}
5 . 导演类知道具体的建造者类建造产品ComputerDirector
类
该类就比较简单了,在该类内部实现两个构造方法,分别对应实现两种计算机的过程,这时候才是正式的建造过程。
package cn.codekong.start;
/**
* 计算机导演类,知道具体建造者建造计算机
*/
public class ComputerDirector {
ComputerBuilder builder;
//建造T410计算机
public T410 constructT410(){
builder = new T410Builder();
builder.buildCpu();
builder.buildRam();
builder.buildHardDisk();
builder.buildGraphicCard();
builder.buildMonitor();
builder.buildOs();
//建造结束将产品返回供外部使用
return (T410)builder.getResult();
}
//建造X201计算机
public X201 constructX201(){
builder = new X201Builder();
builder.buildCpu();
builder.buildRam();
builder.buildHardDisk();
//由于X201没有独立显卡,则不调用buildGraphicCard()函数
//builder.buildGraphicCard();
builder.buildMonitor();
builder.buildOs();
//建造结束将产品返回供外部使用
return (X201)builder.getResult();
}
}
6 . 最后让我们测试一下建造的产品是否是好的,新建测试类ComputerTest
类
package cn.codekong.start;
/**
* 计算机建造测试类
*/
public class ComputerTest {
public static void main(String[] args) {
ComputerDirector computerDirector = new ComputerDirector();
//建造T410计算机
Computer t410 = computerDirector.constructT410();
//输出T410计算机的配置参数
System.out.println(t410);
System.out.println("------------我是分割线----------------");
//建造X201计算机
Computer x201 = computerDirector.constructX201();
//输出X201的计算机配置
System.out.println(x201);
}
}
输出结果如下
型号: Thinkpad T410
CPU i5-450
内存 4G 1333MHz
硬盘 500G 7200转
显卡 Nvidia
显示器 14英寸 1280*800
操作系统 Windows7 旗舰版
------------我是分割线----------------
型号: Thinkpad X201
CPU i3-350
内存 2G 1333M
硬盘 250G 5400 转
显示器 12英寸 1280*800
操作系统 Windows7 Home版
好了,经过上面的步骤,我们的产品就建造好咯。
这时候有人会说,你这个例子还是不接地气啊,我怎么没见过什么程序这么写呢,好,那咋就来一个接地气的例子,看下面的代码:
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("我是标题")
.setIcon(R.drawable.icon)
.show();
上面是一个Android里面警告框的例子,我们可以通过链式调用的方法将标题和图标传入,然后调用show()方法就构建了一个警告框。这个例子是不是很常见,那我们就用一个类使用建造者模式实现一下吧:
package com.codekong.my;
import javax.naming.Context;
public class MyDialog {
//警告框标题
private String title;
//警告框图标资源Id
private int iconId;
//上下文环境
private Context context;
public String getTitle() {
return title;
}
public int getIconId() {
return iconId;
}
public Context getContext() {
return context;
}
public static class Builder{
//设置默认值
private String title = "Title";
private int iconId = 0;
private Context context;
public Builder(Context context){
this.context = context;
}
public Builder setTitle(String title) {
this.title = title;
return this;
}
public Builder setIconId(int iconId) {
this.iconId = iconId;
return this;
}
//应用我们的设置
private void applyConfig(MyDialog myDialog){
myDialog.title = title;
myDialog.iconId = iconId;
myDialog.context = context;
}
public MyDialog show(){
MyDialog myDialog = new MyDialog();
applyConfig(myDialog);
return myDialog;
}
}
}
上面的类主要涉及到以下几步:
1 . 创建一个类,先声明他的成员变量以及成员变量的get方法(其实这一步就是建造者模式里面的产品角色
,get方法是为了我们使用时可以随时查看我们自定义的产品属性)
2 . 定义一个静态内部类Builder
,然后把我们产品定义的属性在静态内部类中复制一份,同时生成它的set方法(这一步呢其实就是我们的抽象建造者角色
,要注意的一点是为了实现链式调用,我们要让我们的set方法返回值为Builder, 同时在set方法中返回this,也就是返回本对象)
3 . 接着定义applyConfig()
方法,把我们通过set方法设置的值全部赋值给我们的外部类对应的成员变量(这一步就是我们的具体的建造者
角色)
4 . 最后对外提供一个show()
方法,在其中先 new 出一个我们的MyDialog对象,然后把它传入调用applyConfig()方法,调用过后我们的myDialog对象就已经被设置属性了,我们此时就可以将这个设置过的对象传到外部供其他类使用(这一步就是我们的导演角色
)
当我们使用的时候就可以通过下面代码使用:
MyDialog myDialog = new MyDialog.Builder(this)
.setTitle("我是标题")
.setIconId(R.drawable.icon)
.show();
六、后记
以上就是建造者模式的全部内容,希望可以帮助到有需要的人.