冰鉴的博客

夫唯不争,故天下莫能与之争

设计模式之建造者模式

建造者模式(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();

六、后记

以上就是建造者模式的全部内容,希望可以帮助到有需要的人.

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/bingjianIT/article/details/53607856
个人分类: 设计模式 java
所属专栏: java设计模式
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭