美文网首页
建造者模式

建造者模式

作者: 做梦枯岛醒 | 来源:发表于2019-12-05 21:24 被阅读0次

对于建造者这个设计模式平时很常见,举个例子Android中的AlertDialog,这个在平时很常用。一段代码如下。

AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("title").setMessage("message").setPositiveButton("ok", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        }).setNegativeButton("cancel", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });

这里使用了Builder方法来构建,set系列函数来设置数据,build来构建返回对象。
常见的建造者模式都是如此的形式,而构建者模式也并没有什么特别之处,现在比较常见的有两种表现形式,其中一种是最原始的构建者模式,另一种是实际工程中常见的设计模式,下面先说下后面一种。

形式1

这里通过一个栗子来说这种模式。
下面是一个App的构建过程,其中分为几个部分:文档,设计,编码,测试,部署这几个部分。具体的解释我写到注释里。

public class App {

    /**
     * 写文档
     */
    private String doc;
    /**
     * 做设计
     */
    private String design;

    /**
     * 编码
     */
    private String coding;
    /**
     * 测试
     */
    private String test;
    /**
     * 部署
     */
    private String deploy;

    //构造方法
    public App(String doc, String design, String coding, String test, String deploy) {
        this.doc = doc;
        this.design = design;
        this.coding = coding;
        this.test = test;
        this.deploy = deploy;
    }

    public App() {
    }

    //提供一个传入构造器的方法,调用build方法才把属性赋值,这里可以实现一个延迟构建的功能
    public App(Builder builder) {
        this.doc = builder.doc;
        this.design = builder.design;
        this.coding = builder.coding;
        this.test = builder.test;
        this.deploy = builder.deploy;
    }

    public String getDoc() {
        return doc;
    }

    public void setDoc(String doc) {
        this.doc = doc;
    }

    public String getDesign() {
        return design;
    }

    public void setDesign(String design) {
        this.design = design;
    }

    public String getCoding() {
        return coding;
    }

    public void setCoding(String coding) {
        this.coding = coding;
    }

    public String getTest() {
        return test;
    }

    public void setTest(String test) {
        this.test = test;
    }

    public String getDeploy() {
        return deploy;
    }

    public void setDeploy(String deploy) {
        this.deploy = deploy;
    }

    //静态内部类,构造器
    public static final class Builder {

        private String doc;
        private String design;
        private String coding;
        private String test;
        private String deploy;

        //一般使用这样的构造方法,当然也支持传参
        public Builder() {

        }

        //构造器属性的初始化,同时return this支持链式调用
        public Builder doc(String doc) {
            this.doc = doc;
            return this;
        }

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

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

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

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

        //build的时候才开始创建App
        public App build() {
            return new App(this);
        }
    }
}

要是不使用设计模式,我们就可以这么来写:

App app = new App("doc","design","coding","test","deploy");

说实话现在写久了反倒觉得new关键字没啥用,如果能像Dart一样就好了,初始化一个App的话,除非提供大量的构造方法重载,否则基本上不可能一行就实现一个可选参数的"App",正常的set方法又太麻烦(不支持链式调用的。

而使用设计模式,就变成了这样。
可选择需要配置的项目,且配置后对象还没有创建,直到我们build的时候才将对象构建出来。

 App.Builder builder = new App.Builder()
                .doc("doc").design("design").coding("coding").test("test").deploy("deploy");
 App app2 = builder.build();

这个场景在我们有大量的属性的时候很方便。

这时候你就会有疑问了,仅仅是把初始化的工作套了一层,好像也没什么卵用,费时费力而且setter方法也可以支持链式调用啊,延迟加载也没啥用并且Builder的创建又会浪费内存,那么构建者模式存在的意义是什么呢?

栗子

下面是几个提出的问题。

  • 举这样一个例子,一个App,可能类型不同,实现的方案不同,比如说抖音要在设计上多花功夫,王者荣耀要在编码性能上下功夫,这就要求对design() 方法,对coding() 方法做特殊处理,这里就出现了2个构建者。
  • 我们写一个实体,一般包括属性,get、set方法,而且这些方法中一般不会做很多逻辑处理,那么逻辑处理交给谁?让调用方处理肯定是不合理的。这个时候构建者的主角光环就出来了,很多错误可以直接在构建之前发现。
  • 类似于上个问题,如果我要严格约束调用顺序,doc一定在design之前,test一定要在coding之后如何操作? 约束限制写在set里面是绝对不合理的,跟上面一条类似。

上面的几个问题已经能够说明构建者模式还是有存在的意思的,但实际上上面这个只是一个简化版的构建者模式,其中Builder扮演者构建者和指挥者的两重角色。

形式2

解决栗子一节的问题1,如果有不同的构建要求怎么办?那么只能说写两个构建者了。先来改造我们的代码。

public class App {

    /**
     * 写文档
     */
    private String doc;
    /**
     * 做设计
     */
    private String design;

    /**
     * 编码
     */
    private String coding;
    /**
     * 测试
     */
    private String test;
    /**
     * 部署
     */
    private String deploy;


    public App(String doc, String design, String coding, String test, String deploy) {
        this.doc = doc;
        this.design = design;
        this.coding = coding;
        this.test = test;
        this.deploy = deploy;
    }

    public App(ByteDanceBuilder builder) {
        this.doc = builder.doc;
        this.design = builder.design;
        this.coding = builder.coding;
        this.test = builder.test;
        this.deploy = builder.deploy;
    }
    
    public App(TimiBuilder builder){
        this.doc = builder.doc;
        this.design = builder.design;
        this.coding = builder.coding;
        this.test = builder.test;
        this.deploy = builder.deploy;
    }
    
    //字节跳动构建者
    public static final class ByteDanceBuilder {

        private String doc;
        private String design;
        private String coding;
        private String test;
        private String deploy;
        

        public ByteDanceBuilder doc(String doc) {
            this.doc = doc;
            return this;
        }

        public ByteDanceBuilder design(String design) {
            this.design = design;
            //if design 不合格 throw 异常
            return this;
        }

        public ByteDanceBuilder coding(String coding) {
            this.coding = coding;
            return this;
        }

        public ByteDanceBuilder test(String test) {
            this.test = test;
            return this;
        }

        public ByteDanceBuilder deploy(String deploy) {
            this.deploy = deploy;
            return this;
        }

        public App build() {
            return new App(this);
        }
    }
    
    //天美工作室构建者
    public static final class TimiBuilder{

        private String doc;
        private String design;
        private String coding;
        private String test;
        private String deploy;

      
        public TimiBuilder doc(String doc) {
            this.doc = doc;
            return this;
        }

        public TimiBuilder design(String design) {
            this.design = design;
            return this;
        }

        public TimiBuilder coding(String coding) {
            this.coding = coding;
            //if coding 不合格 throw 异常
            return this;
        }

        public TimiBuilder test(String test) {
            this.test = test;
            return this;
        }

        public TimiBuilder deploy(String deploy) {
            this.deploy = deploy;
            return this;
        }

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

上面我省略了一些代码,留下关键的和新加的,可以看到代码里已经有两个构建者了,一个是抖音App的字节跳动构建者,另一个是王者荣耀的天美工作室的构建者,而且他们分别对设计和代码质量严格把关。这样App类就要提供两个构造方法来实现构建过程。
使用如下:

  App.ByteDanceBuilder builder1 = new App.ByteDanceBuilder()
                .doc("doc").design("pretty design").coding("coding").test("test").deploy("deploy");
  App app1 = builder1.build();

  App.TimiBuilder builder2 = new App.TimiBuilder()
                .doc("doc").design("design").coding("pretty coding").test("test").deploy("deploy");
  App app2 = builder2.build();

你把改造完的代码提交了,然后后来老板又提出我们的App需要加一个运营的步骤,在部署之后。然后你又屁颠儿的去修改每一个构建者的结构,万一写错了,那岂不是凉凉……于是定义了一个接口约束。

Builder

public interface Builder {

    Builder doc(String doc);

    Builder design(String design);

    Builder coding(String coding);

    Builder test(String test);

    Builder deploy(String deploy);

    Builder operating(String operating);

    App build();
}

ByteDanceBuilder

public class ByteDanceBuilder implements Builder {

    private App app = new App();

    @Override
    public Builder doc(String doc) {
        app.setDoc(doc);
        return this;
    }

    @Override
    public Builder design(String design) {
        app.setDesign(design);
        return this;
    }

    @Override
    public Builder coding(String coding) {
        app.setCoding(coding);
        return this;
    }

    @Override
    public Builder test(String test) {
        app.setTest(test);
        return this;
    }

    @Override
    public Builder deploy(String deploy) {
        app.setDeploy(deploy);
        return this;
    }

    @Override
    public Builder operating(String operating) {
        app.setOperating(operating);
        return this;
    }

    @Override
    public App build() {
        return app;
    }
}

然后修改App类为默认的样子,就是仅有属性和get/set方法。
使用如下:

ByteDanceBuilder builder = new ByteDanceBuilder();
App app = builder.doc("doc").design("design").coding("coding").test("test").deploy("deploy").operating("").build();

可以看到,代码上不一样了,但是使用差不多,要是构建者种类比较多的话,我们要写n多的构造者类,n多种链式调用。

image.pngimage.png

降级款

还有一个很累赘的东西,在这里加上他还不如不加,就是前面有提到过依据的指挥者,指挥者的话对构建者进行了一个管理,说起来很像一个管理员。

public class Director {
    
    public void build(Builder builder) {
        builder.doc("doc");
        builder.design("design");
        builder.coding("coding");
        builder.test("test");
        builder.deploy("deploy");
        builder.operating("");
    }
}

在管理者中执行构建的逻辑,相当于在直接调用的基础上封装了一层(但是并没什么优越之处),调用如下:

ByteDanceBuilder b1 = new ByteDanceBuilder();
Director d = new Director();
d.build(b1);
App app1 = b1.build();  

这个还是将参数封装进去了,要是参数也要传的话……emm,可以想象。


image.pngimage.png

如何选择?

上面提到了2中实现形式,其中第一个是现在比较常用的形式,比如说Okhttp,Retrofit,Notification,AlertDialog等都用到了,直接将Builder封装在实体类或者主类中,而第二种就是默认的形式他的关系图如下。


image.pngimage.png

那么在实际使用中,我们一般是选择第一种,第二种一般是在多个构造者的情况下使用。
至于优缺点简单说下。

优点

  • 使用者不必关心产品内部,将产品本身和产品的创建过程解耦,使得相同的创建过程可以创建不同的产品。
  • 增加新的具体建造者无须修改原有类库的代码,扩展起来相对容易,可以自行控制产品的创建过程,而且也可以控制调用顺序合法性检查等辅助操作。

缺点

  • 建造的产品最好是有很多共同点,要是差异性过大,处理的成分就比较多,不太实用。
  • 如果构建器的种类过多,维护起来很困难。

参考

https://juejin.im/post/5a23bdd36fb9a045272568a6
https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/builder.html

相关文章

  • Builder Pattern in Java

    建造者模式:建造者模式定义建造者模式应用场景实现案例Jdk中的建造者模式建造者模式的优点建造者模式的缺点 建造者模...

  • 设计模式(4) 建造者模式

    什么是建造者模式 经典建造者模式的优缺点 对建造者模式的扩展 什么是建造者模式 建造者模式将一个复杂的对象的构建与...

  • 建造者模式(部件构造)

    目录 建造者模式的理念 从 POJO 到建造者模式的思考 怎么来实现建造者模式 建造者模式在Android源码中的...

  • 【设计模式】之建造者Builder模式

    建造者模式 什么是建造者模式? 建造者模式属于创建型模式的一员,可以控制对象的实例化过程。建造者模式简化了复杂对象...

  • 建造者模式

    一、建造者模式介绍 二、建造者模式代码实例

  • 建造者模式

    建造者模式 首先,建造者模式的封装性很好。使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和...

  • 建造者模式:深入理解建造者模式 ——组装复杂的实例

    目录: 一 建造者模式介绍 1.1 定义 1.2 为什么要用建造者模式(优点)? 1.3 哪些情况不要用建造者模式...

  • 设计模式之建造者模式

    设计模式之建造者模式 Intro 简介 建造者模式: 建造者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加...

  • 一、设计模式(构建模式)——03建造模式与原型模式

    建造者模式 建造型模式用于创建过程稳定,但配置多变的对象。 建造模式的实现要点: 在建造者模式中,指挥者是直接与客...

  • 创建型模式:建造者模式

    个人公众号原文:创建型模式:建造者模式 五大创建型模式之四:建造者模式。 简介 姓名 :建造者模式 英文名 :Bu...

网友评论

      本文标题:建造者模式

      本文链接:https://www.haomeiwen.com/subject/jhbjgctx.html