胡琪

为今天工作,为明天投资,为未来孵化一些东西!

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

本文讲解的Builder模式并非设计模式中定义的经典的Builder模式(即包含指挥者类Director),因为在实际开发和android上的一些知名的开源项目中看到的都是非经典的模式。关于Builder模式的经典模式的定义可参看:https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/builder.html 。

应用场景

  • 隔离复杂对象的创建和使用。
  • 需要创建的类有复杂的内部结构:表现特征为:这个类包含多种不同签名的构造函数或包含大量成员属性和对应的setter方法

使用到建造者模式的知名项目分析

上面2种场景是Builder模式出席最多的场景,尤其是第2个场景:当要创建的一个类对象包含复杂的内部结构的时候,那么此时我们可以考虑使用建造者模式来创建这个类。

还记得上次讲解单例模式时提到过android图片加载框架UIL的初始化代码吗?其实就使用了建造者模式。首先来回顾下UIL的官方demo的初始化代码:

跟进ImageLoaderConfiguration类看下其具体代码:

上面是简化版的,省略了很多方法定义,但是成员属性部分都保留下来了。完整的可以看官方代码:https://github.com/nostra13/Android-Universal-Image-Loader/blob/4c728792d73e964f6d8501f742cda57dd56731c6/library/src/main/java/com/nostra13/universalimageloader/core/ImageLoaderConfiguration.java

从上面的代码结构可以看出ImageLoaderConfiguration类具备如下特征:

  • ImageLoaderConfiguration类的构造函数是私有的,意味着外部无法直接创建该类的实例
  • 包含大量的成员属性这些属性都定义为了final
  • 包含一个内部类Builder且被public statc修饰,该内部内几乎重复包含了其外部类的绝大多数成员属性
  • Builder类的成员变量中与外部类成员一一对应的部分声明为private,且为其提供了setter方法(广义的setter,不一定是setXX这种形式,但其作用是用来设置成员属性的值)
  • Builder内部类类中setter方法的返回值是Builder对象本身
  • Builder内部类包含了一个build方法,在该方法中返回其外部类的实例对象

上面这些特征就是Builder模式实现的模板。从上面特征中的第2条可以看到,ImageLoaderConfiguration类使用Builder模式满足的是应用场景中提到的第2条。即要创建的对象具备复杂的内部结构,也就是包含很多成员属性。在创建该对象的时候我们需要为其设置大量的属性。传统的做法就是直接在该类中提供大量的setter方法,先创建对象,然后调用对象的setter方法来完成这些属性的设置。类似下面的代码:

传统的做法,当我们要描述一个People对象的时候,首先创建一个People对象实例,然后通过大量的setter方法来为其设置属性。比如name,age等,最后才是去真正使用People类的功能函数,比如演讲speech(),写代码writeCode函数等。这么做一个非常不好的地方就是将对象的构建和使用混到了一起。很明显name,age这些属性是在创建一个People对象的时候就应该具备的,我们真正希望调用的是People的功能函数speech和writeCode。但是这么做为了使用功能函数我们在创建了People对象之后还需要先调用大量的非功能函数setter函数来设置其属性。另外一个不好的点就是通过setter方法来设置属性,意味着在程序运行过程中People对象的name,age等信息可以被外部修改。而实际上一旦People对象产生其姓名和年龄理应该是不可更改的。也就是说无法保证运行期间某些成员的不变性。那么现在反过来看前面提到的UIL开源项目中对ImageLoaderConfiguration类的结构的定义就很清楚其为何要使用Builder模式以及使用其作用了。

首先ImageLoaderConfiguration类包含了大量的非功能属性(实际上这也是配置类的必然特性,比如安卓对话框也包含很多属性可以设置),如果使用传统的setter方法,那么会包含大量的非功能函数。所以我们可以考虑将这些属性的设置函数和功能函数分离开来,比如将这些属性设置函数放到其内部的一个内部类Builder类中,由Builder提供这些属性的设置和ImageLoaderConfiguration类的创建,而ImageLoaderConfiguration只需提供真正的功能函数和一些必要的getter函数。这样一个很大的好处就是将对象的构建和表示相分离,使得类本身对外的API接口将更加简洁,尤其是如果该类是直接暴露给用户操作的门面类时更加明显(在UIL中门面类是ImageLoader类),同时如果为了保证成员属性在对象创建之后的不可变性,可以将外部类中的成员都定义为final,只允许获取,不允许修改。修改必须通过Builder类在构建时修改。另外为了使得Builder类中的setter方法可以链式调用,setter方法都返回Builder对象本身,即return this。

使用Builder模式改写上面的People类

了解了Builder模式的规则之后,我们按照这些规则来将上面的People类改为使用Builder模式的代码:

改写需要遵循的Builder模式的规则在上面注释中解释的很详细,总体需要遵循以下规则

  • 外部类People构造函数私有,且其构造函数依赖Builder对象
  • 外部类People中不希望在运行时被修改的属性设置为final,且对这些属性只提供getter方法,不提供setter方法
  • Builder内部类必须声明为public static的
  • Builder内部类只提供setter方法,如果为了链式调用,setter方法返回Builder对象本身,即return this;
  • 在Builder类的build方法中创建外部类People实例,然后将其返回

这样使用上就非常清晰明了了,如下所示:

使用建造者模式的好处

  1. 通过前面的示例很容易看出来,使用建造者模式的一个优点就是对象的构建和表示相分离,尤其是当构建的对象包含大量的非功能属性的时候,比如安卓对话框,将对象的创建单独放到一个类中去维护,能够使得对象对外的功能API更加清晰,简洁
  2. 将对象的成员属性设置为 final的由于不对外提供setter方法,那么外部只能在使用Builder创建该对象的时候设置该属性,能够确保程序在运行期间属性状态不会被修改
打赏

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注