Android知识技术干货

浅析设计模式-建造者模式

2017-08-21  本文已影响115人  RunAlgorithm

定义

建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。

如果一个类的构造非常复杂,并且如果是由很多部件组合而成,而且整体的组装过程比较复杂的话,需要用到构建者。它的使用我认为有两种优势

构成类的部件,可以是多个一样,也可以不一样的,这个根据业务情况灵活选择

我们可以在现实生活中找到这样的模型

比如,要造一台计算机主机,需要CPU、内存卡、显卡、电源等。组装过程也相对复杂,比如一些接线的处理

比如,要造一辆汽车,需要底盘、方向盘、发动机、座椅、轮子等等

比如,要造航空母舰,需要甲板、起飞装置、机库、动力系统、武器库等

这些模型,都有一个共同点,就是主体由多个部分组成,并且主体的创建过程很复杂,有很多可选择的配置,丰富的组装细节

构建者模式的应用,就是把这个复杂的创建过程分离出来单独维护

现在拿组装电脑的案例来理解

构建者模式-现实模型.png

我们只关心需要使用哪些部件,而并不关心组装过程。我们的角色是使用者,组装电脑的人是指挥者,他们怎么组装,我们并不关心。使用者选好配置后,交给指挥者,然后输出一台符合我们预期的电脑产品

简单设计

构建者模式主要有两种形态

有抽象建造者

这个形态比较复杂,可以用来创建不同的复杂的产品,把产品的部件细节放在具体建造者中实现。然后由指挥者完成复杂的组装

这个实现和上面的区别在于,对 Builder 在进行了一次抽象,并增加了一个指挥者。抽象类声明各个部分的创建,不涉及具体的对象的部件的创建。具体的创建交给它的子类具体建造者来实现

可以得到这样的类图

构建者模式-复杂-类图.png

使用者调用 Director 的 construct 方法来完整对象的组装,这个方法内指挥者对象各部件的组装次序

public class Director {
    private AbstractBuilder builder;
    public Director(AbstractBuilder builder) {
        this.builder = builder;
    }

    public Product construct() {
        builder.buildA();
        builder.buildB();
        builder.buildC();
        return builder.getResult();
    }
}

使用者直接向指挥者拿最终的产品,整个有点像抽象工厂模式

构建者模式返回一个完整产品,抽象工厂返回一系列产品。构建者模式侧重一步步构造和组建复杂对象,把结果返回。工厂模式侧重生产不同类型的部件

每种具体的 Builder 都是独立的,到时候需要构建新产品,只需要扩展新的 Builder,实现开闭原则

适用:

无抽象建造者

这个模式的基本角色只有俩:

这个形态,更侧重于对产品的部件进行按需配置。每个产品需要什么样的部件,交给外部使用者 Client 去选择。这个形态承担了大部分开源库工具类的使用配置,我们的实际应用中大量使用了这个方式,因为简单够用

最后的组装过程发生在 build 方法中,该方法内确定了各部件的组装次序

可以有这样的类图

构建者模式-简易-类图.png

适用:

应用实例

构建者模式用得比较多的有两种场景

对话框的创建

一个对话框由多个部件组成,比如标题栏、内容、确定按钮、取消按钮等等。有的页面需要标题栏,有的页面不需要,有的页面需要确定按钮,有的不需要,这些都通过 Builder 来构建这个复杂的对话框

CommonAlertDialogFragment builder = new CommonAlertDialogFragment.Builder(getActivity())
    .setMessage(xxxx)
    .setCancelable(true)
    .setNegativeButtonText(xxx, null)
    .setPositiveButtonText(xxx, null)
    .setOnDismissListener(new OnDismissListener() {
        @Override
        public void onDismiss() {
            ...
        }
    });
CommonAlertDialogFragment dialog = builder.create()

方法多态的改良

实际应用中,也可以使用构建者来解决一个多参数构造类的问题,对一些参数设置默认值,按需配置,可以帮助类减少方法重载导致方法数量膨胀的问题。假设我们要用客户端下载一个文件,需要配置一些下载参数

如果直接用方法多态的方式,如果想要尽可能地覆盖这些条件的组合所有的情况,需要很多的重载方法

void downloadFile(String url, String savePath);
void downloadFile(String url, String savePath, boolean multiThread);
void downlaodFile(String url, String savePath, boolean multiThread, boolean overwrite);
void downloadFile(String url, String savePath, boolean multiThread, boolean overwrite, int retryNum);
void downloadFile(String url, String savePath, int retryNum);
...

如果未来又增加一个新的条件,方法数量又会有膨胀出好几个;而删除一个条件,则会干扰到多个方法

所以,我们用构建者模式来改善它

public static class Builder {
    private String url;
    private String savePath;
    private int retryNum = 0;
    private boolean multiThread = false;
    private boolean overwrite = true;

    public Builder(String url, String savePath) {
        this.url = url;
        this.savePath = savePath;
    }

    public Builder addRetryNum(int retryNum) {
        this.retryNum = retryNum;
        return this;
    }

    public Builder addMultiThread(boolean multiThread) {
        this.multiThread = multiThread;
        return this;
    }

    public Builder addOverwrite(boolean overwrite) {
        this.overwrite = overwrite;
        return this;
    }

    public DownloadParams build() {
        return new DownloadParams(url, savePath, retryNum, callBack, multiThread, overwrite, sync);
    }
}

然后方法缩减为只有一个

void download(DownloadParams params);

这样用到下载的地方,进行按需配置,不用担心方法数过度膨胀难以更新和维护的问题

开源库使用举例

这些开源库,大都使用构建者模式来满足丰富的配置选项

OKHttp 的创建

OKHttp 的创建,OkHttp 作为 HTTP 请求的客户端,需要配置超时时间、是否允许重定向处理、是否允许连接失败重试等

OkHttpClient.Builder builder = new OkHttpClient.Builder()
    .connectTimeout(20, TimeUnit.MILLISECONDS)
    .readTimeout(60, TimeUnit.MILLISECONDS)
    .writeTimeout(60, TimeUnit.MILLISECONDS)
    .followRedirects(true);
    .followSslRedirects(true);
    .retryOnConnectionFailure(true);
OkHttpClient okHttpClient = builder.build();

通过 Builder 来存储 OkHttpClient 的配置,最后使用这些可选配置完成 OkHttp 的创建

ImageLoader 的创建

ImageLoader 的创建,需要配置线程池核心线程大小、磁盘缓存策略、内存缓存的大小

ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(MeiPaiApplication.getApplication())
    .memoryCacheSizePercentage(13)
    .threadPoolSize(Runtime.getRuntime().availableProcessors())
    .denyCacheImageMultipleSizesInMemory()
    .diskCache(new UnlimitedDiscCache(mImageCacheDir, null, new Md5FileNameGenerator()))
    .tasksProcessingOrder(QueueProcessingType.LIFO)
    .build();
ImageLoader imageLoader = ImageLoader.getInstance();    
imageLoader.init(config);

GsonBuilder 的创建

作为部件,不一定非得每个部件都不一样,有的时候,需要过个相同类型的部件构成。比如我们使用 Gson 库来解析 Json 字符串的时候,需要一个 GsonBuilder,它就可以传入多个对象解析器来处理对象的解析

GsonBuilder buider = new GsonBuilder().
    .registerTypeAdapter(MediaBean.class, new MediaBeanDeserializer())
    .registerTypeAdapter(UserBean.class, new UserBeanDeserializer())
    .registerTypeAdapter(LiveBean.class, new LiveBeanDeserializer());
Gson gson = builder.create();
上一篇 下一篇

猜你喜欢

热点阅读