java设计模式(三)建造者模式

开篇废话

周末因懒的原因,停更了两天。今天主要研究建造者模式。畅游各大博客网站,推荐一篇关于建造者模式的博客http://blog.csdn.net/self_study/article/details/51707029,简单明了,条例清晰。如果看不懂我写的,可以尝试去看看别人的。。。

什么是建造者模式

讲建造者模式,这里要提取几个关键词:

  • 复杂对象: 这里简单的体现可以理解为,这里对象的成员数量很多,所以它很复杂。
  • 构建过程: 对成员对象进行赋值,业务逻辑生成等一系列使之成为一个成熟的对象。
  • 表示: 返回这个对象。
  • 构建过程与表示分离: 一般来说,对成员对象的赋值通常在构造函数中,这种方式除了不够灵活之外,还有就是如果成员对象过多,因为构造函数就会显得十分臃肿。所以,我们可以将构建过程(简单的理解为赋值,这里不严谨,只是为了帮助理解概念)封装成独立的方法,然后再返回整个需要的实例对象。

那么建造者模式:就是为了解决生成复杂对象的问题,主要通过将构建过程与表示分离的方法。


如何实现建造者模式

理解了建造者模式后,我们来实现它,就相对简单了。
首先盗图一张:


建造者模式类图

可以看到,这里呈现了四个类,有时候实际开发中会将这个模型进行简化,我们这里先学习理论,因为实际操作时,灵活变动太多了,不适合讲解。但是万变不离其宗,核心思想就是这些。

  • Product,都称这个为产品类,说白了就是我需要生成的类。
  • Builder类,这是一个抽象类或者接口,用来规定构建对象的所需方法。
  • ConcreteBuilder 对builder的抽象方法的实现类,通常对其优化为product的内部类,从而省掉builder的定义。常见的有 AlertDialog.Builder 。
  • Director 调用concrete的方法的类。一般来说,使用的建造者模式时,这个也给省了。

我们先来看一个完整版的。以造汽车为例,现在我们需要一辆,有四个轮子,有四个座椅,有防风玻璃,有方向盘,发动机等等(这里只列举这几样)。

  • 首先我们先看Product类,就是我们要生成的复杂对象,这里就是指小汽车。
public class Car {
    List<String> tyres = new ArrayList<>();
    List<String> carMounts = new ArrayList<>();
    String glass;
    String steeringWheel;
    String engine;

    public void setTyres(String tyres) {
        this.tyres.add(tyres);
    }

    public void setCarMounts(String carMounts) {
        this.carMounts.add(carMounts);
    }

    public void setGlass(String glass) {
        this.glass = glass;
    }

    public void setSteeringWheel(String steeringWheel) {
        this.steeringWheel = steeringWheel;
    }

    public void setEngine(String engine) {
        this.engine = engine;
    }

    public List<String> getTyres() {
        return tyres;
    }

    public List<String> getCarMounts() {
        return carMounts;
    }

    public String getGlass() {
        return glass;
    }

    public String getSteeringWheel() {
        return steeringWheel;
    }

    public String getEngine() {
        return engine;
    }

}

这里可以看到小汽车有五个属性,然后通过setter和getter方法来对属性赋值。

  • 然后我们先看Builder类,这是一个抽象类或者接口,具体使用看情况而定,我今天想用接口表示:
public interface Builder {
    public void buildeTyre(int wheelNumber);
    public void buildCarMounts(int carMountsNumber);
    public void buildGalss();
    public void buildSteeringWheel();
    public void buildEngine();
    public Car build();
}

这里定义了五个造小汽车零件的方法,和一个生成对象方法,因为你不能只造这些零件,还需要把这些零件给组装起来啊。

  • 紧接着我们需要ConcreteBuilder类来实现这些方法啊,毕竟我们是实干家,不能光说不做!concretebuilder
public class CarBuilder implements Builder {
    
    Car car = new  Car();
    @Override
    public void buildeTyre(int wheelNumber) {
        for (int i = 0; i < wheelNumber; i++) {
            car.setTyres("已经造了" + i + "轮子了");
        }
    }

    @Override
    public void buildCarMounts(int carMountsNumber) {
        for (int i = 0; i < carMountsNumber; i++) {
            car.setCarMounts("已经造了" + i + "椅子了");
        }
    }

    @Override
    public void buildGalss() {
        car.setGlass("防风玻璃做好了");
    }

    @Override
    public void buildSteeringWheel() {
        car.setSteeringWheel("方向盘做好了");
    }

    @Override
    public void buildEngine() {
        car.setEngine("发动机已经做好了");
    }

    @Override
    public Car build() {
        return car;
    }

}

这里实现了builder接口,没什么好说的,值得注意的就是,在CarBuilder类中,维护了一个Car对象,实现的所有方法中,去调用Car的set方法,最后build()方法中去返回这个对象。
此时此刻,我们已经能通过CarBuilder类去建造我们所需要的Car对象了。但是毕竟造汽车的工序是及其复杂了,因为我们这里只简单的列举了五项,可能感觉还是比较轻松的,但是,如果有上百道工序的话,难道我们需要每次去造小车的时候都去调用这上百个方法吗。显然,这里构建的过程,我们希望是不透明的,那么我们可以将这个步骤封装起来,于是就有了Director类的出现。

  • 我们希望有一个技术总工来调度负责整个造车的过程。
public class AodiDirecter {
    private Builder builder;

    public AodiDirecter(Builder builder) {
        this.builder = builder;
    }

    public void construct() {
        builder.buildeTyre(4);
        builder.buildCarMounts(4);
        builder.buildEngine();
        builder.buildGalss();
        builder.buildSteeringWheel();
    }
}

这样整个建造者模式就完成了,而我们需要一个奥迪车时,只需要:

public class Buyer {
    public void buy() {
        Builder builder = new CarBuilder();
        AodiDirecter director = new AodiDirecter(builder);
        director.construct();
        Car aodiCar = builder.build();
    }
}

如果说,builder是产线工人们,AodiDirecter 就可以理解为奥迪工程师,工人们只知道制造器件,工程师来统筹设计造车的工序,安排下去后,工人们按命令执行就行了。而对于买家来说,我并看不到你是怎么造的,反正我只要结果就行了。


但是这样往往会很麻烦,我们通常会对其进行精简优化。

看上面对建造者定义了四个功能模块,在实现过程中,是不是有一些,何必多此一举的感觉。这里我盗用了别人的代码,工作繁忙(太懒了)还请谅解。

public class Computer {
    private String CPU;
    private String GPU;
    private String memoryType;
    private int memorySize;
    private String storageType;
    private int storageSize;
    private String screenType;
    private float screenSize;
    private String OSType;

    public static class Builder {
        // Optional parameters - initialize with default values
        private String CPU = "inter-i3";
        private String GPU = "GTX-960";
        private String memoryType = "ddr3 1666MHz";
        private int memorySize = 8;//8GB
        private String storageType = "hdd";
        private int storageSize = 1024;//1TB
        private String screenType = "IPS";
        private float screenSize = 23.8f;
        private String OSType = "Windows 10";

        public Builder() {
        }

        public Builder setCPU(String CPU) {
            this.CPU = CPU;
            return this;
        }

        public Builder setGPU(String GPU) {
            this.GPU = GPU;
            return this;
        }
        public Builder setMemoryType(String memoryType) {
            this.memoryType = memoryType;
            return this;
        }

        public Builder setMemorySize(int memorySize) {
            this.memorySize = memorySize;
            return this;
        }
        public Builder setStorageType(String storageType) {
            this.storageType = storageType;
            return this;
        }

        public Builder setStorageSize(int storageSize) {
            this.storageSize = storageSize;
            return this;
        }

        public Builder setScreenType(String screenType) {
            this.screenType = screenType;
            return this;
        }
        public Builder setScreenSize(float screenSize) {
            this.screenSize = screenSize;
            return this;
        }

        public Builder setOSType(String OSType) {
            this.OSType = OSType;
            return this;
        }


        public Computer create() {
            return new Computer(this);
        }

    }

    private Computer(Builder builder) {
        CPU = builder.CPU;
        GPU = builder.GPU;
        memoryType = builder.memoryType;
        memorySize = builder.memorySize;
        storageType = builder.storageType;
        storageSize = builder.storageSize;
        screenType = builder.screenType;
        screenSize = builder.screenSize;
        OSType = builder.OSType;
    }
}

可以看到,整个建造者模式就是一个类,然而这个类中存在一个静态内部类Builder,这个Builder,其实对应的是建造者模式中的concreteBuilder,Computer对应的就是我们product。去掉了Builder抽象类,因为直接实现了。同时还缺少了Directer,因为这里的设计是暴露构建细节,所以就不需要了。
这种方式的好处就是,过程可控。我们来看看调用。

Computer computer = new Computer.Builder()
        .setCPU("inter-skylake-i7")
        .setGPU("GTX-Titan")
        .setMemoryType("ddr4-2133MHz")
        .setMemorySize(16)
        .setStorageType("ssd")
        .setStorageSize(512)
        .setScreenType("IPS")
        .setScreenSize(28)
        .setOSType("Ubuntu/Window10")
        .create();

这是一种链式调用,也是我们常见的建造者模式。我们可以对每个成员对象进行控制,从而得到我们想要的product。当然,我们看回Computer类的实现,发现,每个成员对象都是有个默认值的。也就是说,你完全可以通过new Computer.Builder().create()来获取默认的实例对象,这基本就是Directer的功能。所以精简下来,发现建造者模式还是挺实用的。
然而在大型的系统中,精简版的建造者模式,并不是很适用,因为它的针对性太强,往往呈现出来的是同一种产品,拓展性并不是很好,而我们使用原始版的话,可以对Directer进行拓展,可以更加多元灵活。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容