设计模式-建造者模式

建造者模式:把类对象的构造与装配分别实现。

单例、工厂、原型这几种模式的重点在于创建出一个个的实例对象来,而这个建造者模式的重点在于对某个对象的组成部分的装配。

在实际开发中,一个类不可能只有简单的几个属性,往往是有大量的属性。创建对象的同时或者之后,给属性也设置好对应的值,这个对象才有使用的价值。否则一个空对象我们用它做什么呢。

如果一个表有20个字段,那么它对应的实体类就有20个属性,如何给这个类的对象的属性赋值呢。要么是利用构造方法在创建对象的同时就赋值,要么就是创建一个空对象,然后用set方法给赋值。
构造器的方法问题就是不够灵活,而set方法就是代码量大。

建造者模式可以完美的解决这个问题。
先看一下建造者模式的一般方式:
这里有三个基本角色:产品(有比较多、复杂的组件),构造者,装配者
这里以电脑这种产品为例:

public class Computer {

    private String cpu;
    private String memery;
    private String screen;

    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", memery='" + memery + '\'' +
                ", screen='" + screen + '\'' +
                '}';
    }
    // 省略get与set方法
}

构造者,用来模拟构造产品的各个组成部分

// 抽象构造者
public abstract class Builder {
    Computer computer = new Computer();

    // 构建cpu
    protected abstract void buildCpu();

    // 构建memery
    protected abstract void buildMemery();

    // 构建screen
    protected abstract void buildScreen();

}
// 具体的建造者
public class HuaweiComputer extends Builder{
    @Override
    protected void buildCpu() {
        computer.setCpu("only amd ...");
    }

    @Override
    protected void buildMemery() {
        computer.setMemery("sangsam  1024G");
    }

    @Override
    protected void buildScreen() {
        computer.setScreen("huawei 27 big screen");
    }
}

public class AppleComputer extends Builder{
    @Override
    protected void buildCpu() {
        computer.setCpu("inter i7");
    }

    @Override
    protected void buildMemery() {
        computer.setMemery("kinston 256G");
    }

    @Override
    protected void buildScreen() {
        computer.setScreen("apple cloure");
    }
}

装配者,不同的产品只是组件不同,但是装配流程是相同的

public class Director {
    private Builder builder;

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

    public Computer createComputer(){
        builder.buildCpu();
        builder.buildMemery();
        builder.buildScreen();
        return builder.computer;
    }
}

测试

public class ClientDemo {
    public static void main(String[] args) {
        // 通过给装配者传递不同的产品构造者来得到不同的产品
//        Director director = new Director(new AppleComputer());
        Director director = new Director(new HuaweiComputer());
        Computer computer = director.createComputer();
        System.out.println(computer);
    }
}

当然从这里我们可以看到这种模式的适用场景,那就是产品应当是同一类产品,产品的组成部分可能很多,各个组成部分可能不相同,但是他们组成这个产品的整理逻辑(装配流程)是相同的。

其实本质上还是封装,我们把不同的部分(组成部分)单独实现,相同的部分(装配)统一实现。

也可以把构造者和装配者这两个角色放在一起来实现,如下:

public abstract class BuilderTotal {
    Computer computer = new Computer();
    // 构造者:构造产品的各个组成部分
    protected abstract void buildCpu();
    protected abstract void buildMemry();
    protected abstract void buildScreen();
    // 装配者:完成组装
    public Computer createCoumputer(){
        this.buildCpu();
        this.buildMemry();
        this.buildScreen();
        return this.computer;
    }
}

不同的产品

public class XiaomiComputer extends BuilderTotal{
    @Override
    protected void buildCpu() {
        computer.setCpu("xiaomi  cpu");
    }

    @Override
    protected void buildMemry() {
        computer.setMemery("xiaomi  memery");
    }

    @Override
    protected void buildScreen() {
        computer.setScreen("xiaomi screen");
    }
}

public class HpComputer extends BuilderTotal{
    @Override
    protected void buildCpu() {
        computer.setCpu("hp special cpu");
    }

    @Override
    protected void buildMemry() {
        computer.setMemery("hp special memery");
    }

    @Override
    protected void buildScreen() {
        computer.setScreen("hp special screen");
    }
}

测试:

public class ClientDemo_02 {
    public static void main(String[] args) {
        BuilderTotal hp = new HpComputer();
        Computer coumputer = hp.createCoumputer();
        System.out.println(coumputer);
        System.out.println("========================");
        BuilderTotal xiaomi = new XiaomiComputer();
        Computer computer = xiaomi.createCoumputer();
        System.out.println(computer);
    }
}

虽然这样是把不同的功能放在同一个类中实现,不符合单一原则,但其实我觉得这样的代码可读性要高一点,不过当属性比较多的时候,也会比较乱。

接下来看一种比较巧妙的方式,利用内部类的方式,对产品的各个部分(属性)进行组装,而且非常灵活。

public class Phone {
    private String brand;
    private String memory;
    private String screen;
    private String camera;
    private String clour;

    // 私有构造方法,创建对象由内部类的方法实现(最后用到)
    private Phone(String brand, String memory, String screen, String camera, String clour) {
        this.brand = brand;
        this.memory = memory;
        this.screen = screen;
        this.camera = camera;
        this.clour = clour;
    }

    // 借助一个内部类,用来构造属性的各个部分,并创建调用它的构造方法创建对象返回给调用者
    public static PhoneBuilder phoneBuilder(){
        return new PhoneBuilder();
    }

    // 静态内部类实现
    public static class PhoneBuilder{
        // 拥有和外部类相同的参数
        private String brand;
        private String memory;
        private String screen;
        private String camera;
        private String clour;
        // 对每一个属性提供一个方法来设置它的值,这其实就是构造组件的部分
        public PhoneBuilder brand(String brand){
            this.brand = brand;
            // 返回自身,这样方便使用链式编程
            return this;
        }
        public PhoneBuilder memory(String memory){
            this.memory = memory;
            return this;
        }
        public PhoneBuilder screen(String screen){
            this.screen = screen;
            return this;
        }
        public PhoneBuilder camera(String camera){
            this.camera = camera;
            return this;
        }
        public PhoneBuilder clour(String clour){
            this.clour = clour;
            return this;
        }
        // 最后提供一个方法,创建产品对象,参数值就是上面的方法接受到的
        public Phone build(){
            return new Phone(brand, memory, screen, camera, clour);
        }
    }

    // 只为了测试方便,忽略
    @Override
    public String toString() {
        return "Phone{" +
                "brand='" + brand + '\'' +
                ", memory='" + memory + '\'' +
                ", screen='" + screen + '\'' +
                ", camera='" + camera + '\'' +
                ", clour='" + clour + '\'' +
                '}';
    }
}

只看这个类的设计会觉得很复杂,但是使用的时候就觉得爽了:

public class PhoneClient {
    public static void main(String[] args) {
        Phone phone = Phone.phoneBuilder()      // 这一步是得到它内部的一个构造器
                .brand("huawei")
                .clour("black")
//                .memory("64G")
//                .camera("1000 wan")
                .screen("18 ch")
                .build();       // 这一步才是真正创建一个Phone对象,并使用上面接受的参数

        System.out.println(phone);
    }
}

可以看到,在得到构造器和利用构造器创建对象中间,都是设置对象的属性值,最爽的地方就在于:随意。想设置几个就设置几个,而且顺序随意,而且代码可读性很好,很明确要给给哪些属性设置什么值。

其实这完全就是lomboc中的注解@Builder的功能。之前只是照着别人用觉得方便,也没有琢磨琢磨。最近学到了这个,觉得这个设计真是巧妙。平时工作中只是简单的crud,动脑筋比较少,多学学这些优秀的东西,真的可以开阔思路。

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

推荐阅读更多精彩内容