建造者模式说白了就是将类的实例化一个个的封装了起来,里面的属性创建相对复杂,所以帮你直接封装好,从而将对象的创建于表示分离出来,对于客户端,可以直接申请自己想要的具体的对象实例,对于对象的创建,可以通关依赖倒转来进行很好的维护,而不需要客户端知道对象的创建的细节。复杂对象多半是指这个类声明了很多的属性,并且属性的之间可能还存在一些前后关系、依赖关系等。建造者模式也属于六个创建型模式之一,其余五个为:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
- 单例模式
- 原型模式
建造者模式定义
建造者模式也叫「生成器模式」,建造者模式又称为生成器模式,是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式的结构及其相关角色
- Builder(抽象建造者):创建一个产品的抽象类或者是接口,一般在抽象建造者中声明两种方法,一种是 buildPartX() 用于创建复杂产品对象的各个部件,另一种方法是 getResult() 用来返回组合好部件之后的复杂产品对象。
- ConcreteBuilder(具体建造者):实现抽象建造者,然后实现复杂产品对象各个部件属性的具体构造和装配方式
- Product(产品角色):就是我们需要构建的复杂产品对象
- Director(指挥者):指挥者主要负责两件事情,一件是将客户端与复杂产品对象的创建隔离,一件是负责安排复杂产品对象的建造顺序,与抽象建造者关联。客户端与指挥者直接进行交互,客户端体现具体要建造的对象类型,然后通过指挥者的构造函数或者 Setter 方法将对象传入指挥者类中。
接下来我们通过简单的代码示例,来感受一下建造者模式在具体业务中的体现。举一个漫威电影中的例子吧,在漫威系列中有很多的超级英雄的角色,我们模拟创建各种各样的超级英雄的角色。作为一名超级英雄,自然符合我们对于复杂产品的定义,自身携带各种各样的技能属性等。我们先来声明一下超级英雄这个复杂的产品。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
package learn.design.patterns.builder; /** * 建造者模式之复杂产品-漫威超级英雄 * * @ClassName: SuperHero * @author: Glorze * @since: 2020/8/19 17:35 */ public class SuperHero { /** * 超级英雄的类型 * 地球人 * 外星人 * 天神 */ private String type; /** * 性别 * 男 * 女 */ private String sex; /** * 武器 */ private String weapon; /** * 技能 */ private String skill; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getWeapon() { return weapon; } public void setWeapon(String weapon) { this.weapon = weapon; } public String getSkill() { return skill; } public void setSkill(String skill) { this.skill = skill; } } |
接着我们生命抽象建造者,在抽象建造者中声明各个属性的构建方法以及返回一个超级英雄的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package learn.design.patterns.builder; /** * 建造者模式之漫威英雄建造者接口 * * @InterfaceName: SuperHeroBuilder * @author: Glorze * @since: 2020/8/19 17:40 */ public abstract class SuperHeroBuilder { protected SuperHero superHero = new SuperHero(); abstract void buildType(); abstract void buildSex(); abstract void buildWeapon(); abstract void buildSkill(); public SuperHero createSuperHero() { return superHero; } } |
然后,我们写几个具体的建造者,分别是钢铁侠、黑寡妇和绿巨人,在具体的建造者一次实现他们的类型、性别、武器以及技能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package learn.design.patterns.builder; /** * 具体建造者 * 创建一个钢铁侠英雄角色 * * @ClassName: IronMan * @author: Glorze * @since: 2020/8/19 17:44 */ public class IronMan extends SuperHeroBuilder { @Override void buildType() { superHero.setType("地球人"); } @Override void buildSex() { superHero.setSex("男"); } @Override void buildWeapon() { superHero.setWeapon("弑杀者装甲"); } @Override void buildSkill() { superHero.setSkill("电气工程、机械工程"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package learn.design.patterns.builder; /** * 具体建造者 * 创建一个黑寡妇英雄角色 * * @ClassName: BlackWidow * @author: Glorze * @since: 2020/8/19 17:45 */ public class BlackWidow extends SuperHeroBuilder { @Override void buildType() { superHero.setType("地球人"); } @Override void buildSex() { superHero.setSex("女"); } @Override void buildWeapon() { superHero.setWeapon("制服诱惑"); } @Override void buildSkill() { superHero.setSkill("寿命长"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package learn.design.patterns.builder; /** * TODO * * @ClassName: Hulk * @author: Glorze * @since: 2020/8/19 17:45 */ public class Hulk extends SuperHeroBuilder { @Override void buildType() { superHero.setType("地球人"); } @Override void buildSex() { superHero.setSex("男"); } @Override void buildWeapon() { superHero.setWeapon("电缆蔓"); } @Override void buildSkill() { superHero.setSkill("徒手搏斗"); } } |
然后我们创建指挥者,在指挥者中,通过抽象的建造者以及相关的业务顺序,来对超级英雄实现建造,最后将超级英雄返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package learn.design.patterns.builder; /** * 建造者模式之超级英雄指挥者 * * @ClassName: SuperHeroController * @author: Glorze * @since: 2020/8/20 14:19 */ public class SuperHeroController { public SuperHero construct(SuperHeroBuilder superHeroBuilder) { SuperHero superHero; superHeroBuilder.buildSex(); superHeroBuilder.buildType(); superHeroBuilder.buildSkill(); superHeroBuilder.buildWeapon(); superHero = superHeroBuilder.createSuperHero(); return superHero; } public static void main(String[] args) { SuperHeroBuilder superHeroBuilder = new BlackWidow(); SuperHeroController superHeroController = new SuperHeroController(); SuperHero superHero = superHeroController.construct(superHeroBuilder); System.out.println("性别:" + superHero.getSex()); System.out.println("类型:" + superHero.getType()); System.out.println("武器:" + superHero.getWeapon()); System.out.println("技能:" + superHero.getSkill()); } } |
如上述代码所示,我们在客户端直接请求对「黑寡妇」的建造,运行结果如下:
建造者模式优缺点及适用场景
建造者模式的优点
- 指挥者、客户端都不需要知道复杂产品的内部细节,复杂产品的创建都交给具体的建造者处理即可,当然,可能建造者的处理过程会相对复杂,不过实现了复杂产品对象的创建与使用的解耦。
- 建造者模式可以对复杂产品的创建进行精细控制,这可以在指挥者中事先这样的逻辑。
- 因为每一个具体建造者都是独立的,所以符合「开放-封闭原则」。
建造者模式的缺点
- 建造者模式只适合产品相对统一性比较高的对象,也就是创建的都是一类产品,比如代码中的超级英雄这样,都需要指定他们的武器以及技能等。如果需要创建的产品彼此之间差异比较大,则不适合使用建造者模式。
- 多个具体的建造者会随着业务的扩展是系统变得复杂,不易维护。
建造者模式的适用场景
- 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
- 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
- 对象的创建过程独立于创建该对象的类。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
建造者模式与抽象工厂模式的比较
看到这里,其实我们会觉得这个建造者模式其实是跟工厂系列模式有一些相似的,都是对产品进行封装处理,然后客户端直接使用就可以了,不需要解除或者说去研究产品的创建、实例化过程。但是建造者模式跟工厂系列模式的本质区别是,建造者模式返回的是一个完整地、具体地的复杂产品对象,而
工厂系列模式其实返回的系列产品。怎么硕呢,工厂类似于生产大批量的工具,根据这些工具可以组合成各样各样的产品。而建造者是根据某一个产品,通过产品对应的配件来进行具体的构造并返回。
建造者模式在 MyBatis 中的使用
建造者模式在 MyBatis 中的应用极其广泛,MyBatis 也是建造者模式的集中体现。在 MyBtis 的核心处理层之 MyBatis 的初始化过程中,就主要使用了建造者模式,用来加载解析 mybatis-config.xml 文件、映射配置文件以及相关的注解信息。MyBatis 初始化的抽象建造者接口叫做「BaseBuilder」,声明如下主要三个成员变量:
- Configuration configuration
- TypeAliasRegistry typeAliasRegistry
- TypeHandlerRegistry typeHandlerRegistry
Configuration 是 MyBatis 初始化过程的核心对象,在 MyBatis 中几乎全部的配置信息会保存到 Configuration 对象中。Configuration 对象是在 MyBatis 初始化过程中创建且是全局唯一的,也叫「All-In-One」 配置对象;在 mybatis-config.xml 配置文件中可以使用 <typeAliases> 标签定义别名,这些定义的别名都会记录在该 TypeAliasRegistry 对象中;在 mybatis-config.xml 在配置文件中可以使用 <typeHandlers> 标签添加自定义 TypeHandler 处理器,完成指定数据库类型与 Java 类型的转换这些 TypeHandler 都会记录在 TypeHandlerRegistry 中。
根据这个抽象建造者,下面的实现类各司其职,实现对应的 mybatis-config.xml文件解析、映射配置文件解析、SQL 语句节点解析、Mapper 接口绑定等具体功能。所以 BaseBuilder 的主要实现类集中在如下几个类中,他们就是具体的建造者。
- XMLConfigBuilder:主要负责解析 mybatis-config.xml 配置文件,boolean parsed 标识是否已经解析过 mybatis-config.xml 配置文件;XPathParser parser 用于解析 mybatis-config.xml 配置文件的 XPathParser 对象;String environment 标识 <environment> 配置的名称,默认读取 <environment> 标签的 default 属性;ReflectorFactory localReflectorFactory 负责创建和缓存 Reflector 对象。
- XMLMapperBuilder:负责解析映射配置文件,也就是那些、cache、cache-ref、parameterMap、cresultMap、sql 等节点的解析工作。
- XMLStatementBuilder:解析主要用于定义 SQL 语句的 <SQL> 节点,如 <include>、<selectKey> 等节点。
另外在解析 MyBatis 配置文件的过程中,对于各种各样的节点解析,也大量使用了建造者模式,因为很容易理解,配置文件里面的这些东西基本都需要一一解析并且映射到 MyBatis 声明的对象中去,所以建造者模式在这里会运用的非常多。比如处理缓存节点的 CacheBuilder、ResultMapping.Builder、XMLScriptBuilder(创建 SqlSource)等等。
相关文章阅读
更博不易,如果觉得文章对你有帮助并且有能力的老铁烦请捐赠盒烟钱,点我去赞助。或者扫描文章下面的微信/支付宝二维码打赏任意金额(点击「给你买杜蕾斯」),也可以加入本站封闭式交流论坛「DownHub」开启新世界的大门,老四这里抱拳谢谢诸位了。捐赠时请备注姓名或者昵称,因为您的署名会出现在赞赏列表页面,您的捐赠钱财也会被用于小站的服务器运维上面,再次抱拳感谢。