设计模式之建造者模式

这是创建型设计模式的最后一种模式,也是用来创建复杂的对象。

网上大多数例子,在讲解建造者模式时,都是用KFC套餐、做一道硬菜或者组装电脑等来举例,我们今天换一个例子,和我们程序员更贴近的例子。

俗话说:人在IT飘,哪能不通宵。

通宵多了之后,身体也感觉被渐渐掏空了,这个时候如果不注意保护身体,那么离ICU就不远了,我们程序员如何远离ICU呢?

1)磨练好技术,提升工作效率,减少加班。(安利一波:学技术哪家强,IT之美强中强,www.itzhimei.com)

2)定期体检了,用科学的检查提前预防疾病的发生。

今天的例子就以体检来演示。

体检我们都知道有很多项目,比如检查身高、体重、耳鼻喉、血压、心电图、菊花(gang men)等等十几项,有的体检甚至几十项,这些检查完成后,就是生成报告了。

我们用代码来对应报告,报告的bean定义为:

public class ExaminationReport {
    private String name;
    private String height;
    private String weight;
    private String heart;
    private String ct;
    private String eyes;
    private String mouth;
    //喉咙
    private String throat;
    //血压
    private String bloodPressure;
    //菊花--你懂得
    private String juhua;

    ......这里略去十几项检查
}

我们要生成一个报告数据,要怎么生成呢?

一般的方式是不是在这个报告里,给每个属性生成set方法,new 一个ExaminationReport对象,然后写一大堆setXXX来赋值。

用上面的方法,就面临一个最重要的问题,就是这个体检报告是每个人的隐私,报告一旦生成,不希望被别人篡改,bean中有set方法,就意味着可以随便修改报告结果。

有同学说,可以用构造器。是的可以,但是因为参数太多,构造器的参数也会非常多,用起来也非常麻烦,像这种:

public ExaminationReport(String name,String height,String weight,
String heart,String ct,String eyes,
String mouth,String throat...) {......}

而且,这种方法还有一个问题是有些参数是必须、有些是不是必须的,那要根据每种情况,写对应的构造器,依旧非常麻烦。

那么,建造者模式就登场了,建造者模式就是让复杂对象的创建更加方便简单。

主要代码都在报告Bean中

import org.apache.commons.lang.StringUtils;

/**
 * 体检报告Bean
 */
public class ExaminationReport {
    private String name;
    private String height;
    private String weight;
    private String heart;
    private String ct;
    private String eyes;
    private String mouth;
    //喉咙
    private String throat;
    //血压
    private String bloodPressure;
    //菊花--你懂得
    private String juhua;

    //public ExaminationReport(String name,String height,String weight,String heart,String ct,String eyes,String mouth,String throat...)

    private ExaminationReport(ExaminationReportBuilder builder) {
        this.name = builder.name;
        this.height = builder.height;
        this.weight = builder.weight;
        this.heart = builder.heart;
        this.ct = builder.ct;
        this.eyes = builder.eyes;
        this.mouth = builder.mouth;
        this.throat = builder.throat;
        this.bloodPressure = builder.bloodPressure;
        this.juhua = builder.juhua;
    }

    public void printReport() {
        System.out.println("***************体检报告***************");
        System.out.println(this.name + "体检报告:");
        System.out.println("身高:" + (StringUtils.isEmpty(this.height)?"---":this.height));
        System.out.println("体重:"+ (StringUtils.isEmpty(this.weight)?"---":this.weight));
        System.out.println("心率:"+ (StringUtils.isEmpty(this.heart)?"---":this.heart));
        System.out.println("CT:"+ (StringUtils.isEmpty(this.ct)?"---":this.ct));
        System.out.println("视力:"+ (StringUtils.isEmpty(this.eyes)?"---":this.eyes));
        System.out.println("口腔:"+ (StringUtils.isEmpty(this.mouth)?"---":this.mouth));
        System.out.println("喉咙:"+ (StringUtils.isEmpty(this.throat)?"---":this.throat));
        System.out.println("血压:"+ (StringUtils.isEmpty(this.bloodPressure)?"---":this.bloodPressure));
        System.out.println("菊花:"+ (StringUtils.isEmpty(this.juhua)?"---":this.juhua));
        System.out.println("**************************************");
    }

    public static class ExaminationReportBuilder {
        private String name;
        private String height;
        private String weight;
        private String heart;
        private String ct;
        private String eyes;
        private String mouth;
        private String throat;
        private String bloodPressure;
        private String juhua;

        public ExaminationReportBuilder(String name) {
            this.name = name;
        }
        public ExaminationReportBuilder setHeight(String height) {
            this.height = height;
            return this;
        }

        public ExaminationReportBuilder setWeight(String weight) {
            this.weight = weight;
            return this;
        }

        public ExaminationReportBuilder setHeart(String heart) {
            this.heart = heart;
            return this;
        }

        public ExaminationReportBuilder setCt(String ct) {
            this.ct = ct;
            return this;
        }

        public ExaminationReportBuilder setEyes(String eyes) {
            this.eyes = eyes;
            return this;
        }

        public ExaminationReportBuilder setMouth(String mouth) {
            this.mouth = mouth;
            return this;
        }

        public ExaminationReportBuilder setThroat(String throat) {
            this.throat = throat;
            return this;
        }

        public ExaminationReportBuilder setBloodPressure(String bloodPressure) {
            this.bloodPressure = bloodPressure;
            return this;
        }

        public ExaminationReportBuilder setJuhua(String juhua) {
            this.juhua = juhua;
            return this;
        }

        public ExaminationReport build() {
            return new ExaminationReport(this);
        }
    }
}

我们来具体看一下代码:

1、ExaminationReport就是报告bean,有各种检查结果属性,有打印报告的方法。

2、ExaminationReport有一个静态内部类:ExaminationReportBuilder

这个内部类作用就是ExaminationReport对象构建的实际建造者,在ExaminationReportBuilder中,每个成员方法都构建一个ExaminationReport的报告属性,并且这每个方法都返回ExaminationReportBuilder对象,为的是能够使用类似Lambda的语法,一直点出来各种属性去连续设置属性,效果如下:

ExaminationReport zhangsan = new ExaminationReport.ExaminationReportBuilder("张三")
                .setHeight("170")
                .setWeight("150")
                .setHeart("70")
                .setEyes("5.0")
                .setMouth("正常")
                .setBloodPressure("120-90")
                .build();

3、ExaminationReportBuilder最后的build方法

public ExaminationReport build() {
      return new ExaminationReport(this);
}

new出ExaminationReport,并将自己作为参数传入ExaminationReport构造器,也就是将set的各种值,传入到了ExaminationReport的构造器中进行构造,ExaminationReport的构造器中是这样用ExaminationReportBuilder的值的:

private ExaminationReport(ExaminationReportBuilder builder) {
        this.name = builder.name;
        this.height = builder.height;
        this.weight = builder.weight;
        this.heart = builder.heart;
        this.ct = builder.ct;
        this.eyes = builder.eyes;
        this.mouth = builder.mouth;
        this.throat = builder.throat;
        this.bloodPressure = builder.bloodPressure;
        this.juhua = builder.juhua;
    }

这就实现了优雅的建造复杂对象的效果。

测试代码:

/**
 * www.itzhimei.com
 */
public class Client {

    public static void main(String[] args) {
        ExaminationReport zhangsan = new ExaminationReport.ExaminationReportBuilder("张三")
                .setHeight("170")
                .setWeight("150")
                .setHeart("70")
                .setEyes("5.0")
                .setMouth("正常")
                .setBloodPressure("120-90")
                .build();
        zhangsan.printReport();

        ExaminationReport xiaoming = new ExaminationReport.ExaminationReportBuilder("小明")
                .setHeight("175")
                .setWeight("165")
                .setHeight("80")
                .setEyes("4.5")
                .setMouth("正常")
                .setThroat("正常")
                .setBloodPressure("120-90")
                .setJuhua("痔疮")
                .build();
        zhangsan.printReport();

    }
}

输出结果:

***************体检报告***************
张三体检报告:
身高:170
体重:150
心率:70
CT:---
视力:5.0
口腔:正常
喉咙:---
血压:120-90
菊花:---
**************************************

***************体检报告***************
张三体检报告:
身高:170
体重:150
心率:70
CT:---
视力:5.0
口腔:正常
喉咙:---
血压:120-90
菊花:---
**************************************

总结:

建造者模式就是让复杂对象的创建更加方便简单,将原本直接去new的对象,然后不停setXXX,修改为将这些工作交给一个职责类来处理,这是建造者的本质。