设计模式之建造者模式

序言

在看Retrofit源码时,可以看到里面用到了大量的设计模式,如果我们非常了解设计模式对理解是很有帮助的,在Rerofit里有用到建造者模式。

定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以生成不同的表示。建造者模式也是对象创建行模式。

说明: 单看这个定义初学者也是很蒙, 大概意思是不需要关心复杂对象创建过程,只需要知道建造者类型即可,建造者才关心这个复杂对象是如何一步一步创建一个复杂对象的。 根据不同类型的建造者生成不同的复杂的对象。也是有具有良好的扩展性。

建造者模式类图

设计模式之建造者模式_第1张图片
建造者模式类图

在建造者模式中包含如下几个角色:

  • Builder(抽象的建造者) : 它是创建Product对象各个部分的抽象接口。 在该接口一般有两类接口 ,一种是biuldPartX()方法,用于创建该复杂对象各个部分; 另一种方法是getResult, 它用于返回这个复杂对象。 Build可以是一个接口,也可以是抽象类。
  • ConcreteBuilder(具体的建造者): 它实现了Builder接口,具体实现该复杂对象的各个部分,然后返回具体的复杂对象。
  • Product(产品角色): 它是复杂对象,里面包含该对象有哪些部分组成。
  • Director(指挥者):它负责对象的建造次序。指挥者和抽象的建造者存在关联关系,可以在construct中完成复杂对象的创建过程。

上面有提到复杂对象,那什么是复杂对象呢,简单来说就是包含多个成员属性的对象。代码示例如下:

//产品
class Product{
    private String partA;//定义产品的属性,属性可以是其它类型, 值类型和引用类型都行。
    private String partB;
    private String partC;
    //Setter和Getter方法省略就不写了
}

在抽象建造者定义产品的创建方法和获取产品对象的方法。代码示例如下:

//抽象建造者
interface Builder{
    void buildPartA();//创建产品的属性
    void buildPartB();
    void buildPartC();
    Product getResult(); //返回产品对象

}

抽象建造者中申明了一系列的buildPartX()方法,但是具体的实现还是需要具体的建造者。 不同的建造者实现的buildPartX有所区别。代码示例如下:

//具体的建造者 A
public class AConcreteBuilder implements Builder {
Product productA=new Product();
@Override
public void buildPartA() {
    productA.setPartA("PartA from AConcreteBuilder");
}

@Override
public void buildPartB() {
    productA.setPartA("PartB from AConcreteBuilder");
}

@Override
public void buildPartC() {
    productA.setPartA("PartC from AConcreteBuilder");
}

@Override
public Product getResult() {
    return productA;
}
}

//具体的建造者 B
public class BConcreteBuilder implements Build {
Product productB=new Product();
@Override
public void buildPartA() {
    productB.setPartA("PartA from BConcreteBuilder");
}

@Override
public void buildPartB() {
    productB.setPartA("PartB from BConcreteBuilder");
}

@Override
public void buildPartC() {
    productB.setPartA("PartC from BConcreteBuilder");
}

@Override
public Product getResult() {
    return productB;
}
}

我们可以看到每个建造者实现的东西是不同的。但是返回的产品还是一类的,只是产品的属性不一样。

在建造者模式还有个Director角色(导演者或指挥者),该类主要有两个作用: 其一就是控制产品的创建过程,包括biuldPartX方法是否被调用以及调用的顺序; 其二 就是隔离了客户与创建的过程, Director只需要指挥抽象的建造者(Build),客户端需要具体的建造者然后调用指挥者的相关方法,返回一个完整的对象(Product)。 代码示例如下

//指挥者
public class Director {
private Builder mBuilder; //抽象的建造者

public Director(Builder build) {
    this.mBuilder = build;
}
public Product construct(){
    mBuilder.buildPartA();
    mBuilder.buildPartB();
    mBuilder.buildPartC();
    return  mBuilder.getResult();
}
}

//客户端   代码片段如下
Builder Abuilder=new AConcreteBuilder();//创建具体的建造者
Director director=new Director(Abuilder);//创建指挥者
Product productA=director.construct();//返回产品A

Builder Bbuilder=new BConcreteBuilder();//创建具体的建造者
Director director=new Director(Bbuilder);//创建指挥者
Product productA=director.construct();//返回产品B

例子

如某个公司要开发个APP(Product),就需要开发者(Builder), 如果有多端的话就需要不同的开发者(ConcreteBuilder),技术经理(Director)就充当指挥者来指示不同的开发者来开发。

APP (Product)

public class APP {
    String code;//代码
    String note;//注释及文档

    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getNote() {
        return note;
    }
    public void setNote(String note) {
        this.note = note;
    }

    @Override
    public String toString() {
        return "APP = {" +
                "code='" + code + '\'' +
                ", note='" + note + '\'' +
                '}';
    }
}

开发者 (Builder)

public interface Developer {
    void writeCode();//写代码

    void writeNote();//写文档及注释

    APP getAPP();//返回APP
}

Andoird开发者(ConcreteBuilder)

public class AndroidDeveloper implements Developer {

    APP app = new APP();

    @Override
    public void writeCode() {
        app.setCode("Android code");
    }
    @Override
    public void writeNote() {
        app.setNote("Android note ");
    }
    @Override
    public APP getAPP() {
        return app;
    }
}

技术经理 (指挥者)

public class TechnicalManager {

    Developer mDeveloper;

    public void setDeveloper(Developer mDeveloper) {
        this.mDeveloper = mDeveloper;
    }

    public APP getAPP() {
        mDeveloper.writeCode();
        mDeveloper.writeNote();
        return mDeveloper.getAPP();
    }
}

测试:

    public static void main(String[] args) {
        TechnicalManager manager = new TechnicalManager();//创建技术经理对象(指挥者)
        AndroidDeveloper javaDeveloper = new AndroidDeveloper();//创建Android开发者(具体的建造者)
        manager.setDeveloper(javaDeveloper);//技术经理指挥Android程序员去干活
        APP app = manager.getAPP(); //完成 Android端 APP
        System.out.println(app);
    }

运行结果:

APP = {code='Android code', note='Android note'}

如果公司想在苹果商店也要上款APP,那就需要ios开发,这样我们就不用修改源代码,只需要另创建一个ios的建造者就OK。

ios 开发者(ConcreteBuilder)

public class IosDeveloper implements Developer {
    APP app = new APP();

    @Override
    public void writeCode() {
    app.setCode("ios code");
    }

    @Override
    public void writeNote() {
        app.setNote("ios note");
    }

    @Override
    public APP getAPP() {
        return app;
    }
}

测试:

  public static void main(String[] args) {
    TechnicalManager manager = new TechnicalManager();//创建技术经理对象(指挥者)
    IosDeveloper iosDeveloper = new IosDeveloper();//创建ios开发者(具体的建造者)
    manager.setDeveloper(iosDeveloper);//技术经理指挥ios程序员去干活
    APP app = manager.getAPP(); //完成 ios端 APP
    System.out.println(app);
}

运行结果:

APP {code='ios code', note='ios note'}

如果需要其他端的APP,只需另创建个建造者就可以完成APP。

但是我们看大多数的源码,里面的建造者模式是没有指挥者的(Director),这个又是怎样实现的呢。 在Android中的弹框(dialog)的实现就用了建造者模式,我们就大致实现下它是怎么创建的。 源码中还是很复杂的,这里只是简单实现。

在Android中创建一个dialog的代码

   AlertDialog.Builder builder = new AlertDialog.Builder(this)
    .setTitle("提示")
    .setMessage("消息")
    .setIcon(R.drawable.icon);
    AlertDialog alertDialog=builder.create(); 

这是Android创建dialog最简单的方式。 现在我们就实现这样的写法。

// Product
public class AlertDialog{
    String msg;//消息
    String tilte;//标题
    int icon; //图标

    @Override
    public String toString() {
        return "AlertDialog{" +
                "msg='" + msg + '\'' +
                ", tilte='" + tilte + '\'' +
                ", icon=" + icon +
                '}';
}
//建造者  (Builder)
    static class Builder{
        private AlertDialog mDialog=new AlertDialog();

        public Builder setIcon(int icon){
            mDialog.icon=icon;
            return this;
        }

        public  Builder setTitle(String title){
        mDialog.tilte=title;
            return this;
        }

        public  Builder setMsg(String msg){
            mDialog.msg=msg;
            return this;
        }

        public AlertDialog create(){
            return  mDialog;
        }
    }
}

测试 :

public static void main(String args[]) {
    //因为Builder是AlertDialog的成员内部类,所以这样调用AlertDialog.Builder  
    AlertDialog.Builder builder = new AlertDialog.Builder();
    builder.setIcon(1)
            .setMsg("消息")
            .setTitle("提示");
    AlertDialog dialog = builder.create();
    System.out.println(dialog);
}

运行结果:

AlertDialog{msg='消息', tilte='提示', icon=1}

可以看到我们也按照Android源码这样的调用方法创建了自己的dialog。 Android中的dialog实现比我这复杂,dialog属性更多,逻辑更复杂。

我们可以再看看Retrofit中使用的建造者模式

   Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(API_URL)
    .addConverterFactory(GsonConverterFactory.create())
    .build();

这是创建Retrofit对象。可以看到也是用了建造者模式。 这里就不说了,下次分析源码时再说。

总结:建造者模式只要明白四个角色Product、Builder、ConcreteBuilder、Director的作用,就差不多明白建造者模式是怎么回事了。

优点:

  • 将产品的本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 每一个具体建造者都是相对独立的,不依赖其他的具体建造者,可以方便地替换和增加新的具体建造者,用户使用不同的具体建造者就能得到不同的产品对象。 由于指挥者(Director)是针对抽象的建造者(Builder)编程,增加新的建造者无需修改原来的建造者,符合‘开闭原则’。

缺点:

  • 所创建的产品具有较多的共同点,其组成部分相似。 如果产品之间的差异性很大的话,就不适用建造者模式。
  • 产品的内部变化复杂的话,可能会导致定义很多的具体建造者来应对这样的变化,这样会导致系统的复杂性。
一个游戏技术人,分享Java、Python、H5、Android 等知识。同时会分享游戏行业日常。

你可能感兴趣的:(设计模式之建造者模式)