建造者模式
(Builder Pattern)
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
- List item分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况下使用。
- 由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
- 建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。
应用场景:
- java中的StringBuilder类的append方法;
- SQL中的PreparedStatement;
- JDOM中,DomBuilder、SAXBuilder;
- 去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐";
- 一个对象有非常复杂的内部结构,想把复杂对象的创建和使用分离
优点:
- 封装性好,创建和使用分离;无需修改原有代码,符合“开闭原则”。
- 拓展性好,建造类之间独立,一定程度上解耦;代码解耦,模块化,方便维护。产品本身与创建过程解耦,可以使用相同的创建过程得到不同的产品。
缺点:
- 产品必须有共同点,使用范围有限。建造者模式创造出来的产品,其组成部分基本相同。如果产品之间的差异较大,则不适用这个模式。
- 若产品内部发生变化,建造者都要修改,成本较大;若内部变化复杂,会有很多的建造类。
与工厂模式的区别
建造者更关注零件的装配顺序的细节,工厂是关心整体
工厂生产的是单一性质的一模一样的产品,而建造者是由各个部件复合而成的复合产品;
Mybatis中用到的建造者模式:SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder等。
比如:XMLConfigBuilder读取配置文件构建出Configuration对象,然后SqlSessionFactoryBuilder使用Configuration对象作为参数,构建出SqlSessionFactory对象。
原因:这么做的原因是Mybatis的初始化工作较复杂,不是一个构造函数就能包括的。所以采用了分层构建方法。例如Mybatis中极其重要的Configuration对象,它庞大且复杂,初始化比较麻烦,所以使用专门的建造者XMLConfigBuilder进行构建。
四种角色
● Product产品类
通常是实现了模板方法模式,也就是有模板方法和基本方法。
● Builder抽象建造者
规范产品的组建,一般是由子类实现。
● ConcreteBuilder具体建造者
实现抽象类定义的所有方法,并且返回一个组建好的对象。
● Director导演类
负责安排已有模块的顺序,然后告诉Builder开始建造,
多个参数的构造方法 其实也可以看作 Director角色放到 ConcreteBuilder具体建造者 中
实例:
比如:建造者模式可以用于描述KFC如何创建套餐:
套餐是一个复杂对象,它一般包含主食(如汉堡、鸡肉卷等)和饮料(如果汁、可乐等)等组成部分,不同的套餐有不同的组成部分,而KFC的服务员可以根据顾客的要求,一步一步装配这些组成部分,构造一份完整的套餐,然后返回给顾客。
-
定义产品角色
具体的产品对象
/** * 一个具体的产品对象,实体 */ public class Meal { private String food; private String drink; public String getFood() { return food; } public void setFood(String food) { this.food = food; } public String getDrink() { return drink; } public void setDrink(String drink) { this.drink = drink; } }
-
定义抽象类 Builder建造者
创建各个部件指定的抽象接口
/** * 抽象建造者 */ public abstract class MealBuilder { Meal meal = new Meal(); public abstract void buildFood(); public abstract void buildDrink(); public Meal getMeal(){ return meal; } }
-
定义ConcreteBuilder(具体建造者)
实现抽象接口,构建和装配各个部件
/** * 具体建造者:A套餐 */ public class MealA extends MealBuilder{ public void buildDrink() { meal.setDrink("可乐"); } public void buildFood() { meal.setFood("薯条"); } } /** * 具体建造者:B套餐 */ public class MealB extends MealBuilder{ public void buildDrink() { meal.setDrink("柠檬果汁"); } public void buildFood() { meal.setFood("鸡翅"); } }
-
定义Director
构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用
一是:隔离了客户与对象的生产过程
二是:负责控制产品对象的生产过程
@AllArgsConstructor @NoArgsConstructor public class KFCServer { private MealBuilder mealBuilder; public Meal construct() { mealBuilder.buildDrink(); mealBuilder.buildFood(); return mealBuilder.getMeal(); } }
-
测试使用
@Test public void construct() { // MealBuilder type = new MealA(); MealBuilder type = new MealB(); KFCServer kfcServer = new KFCServer(type); Meal meal = kfcServer.construct(); System.out.println(meal.getDrink()); System.out.println(meal.getFood()); }
总结:
需要创建的对象比较复杂,需要按步骤一步一步的装配封装
针对一个复杂的对象,抽象出这个对象的builder建造者抽象类,定义好装配封装的方法;
由不同的子类继承建造者实现方法,最后有一个Director管理这些子类,将对象的具体调用分装步骤,封装到方法里,用户使用Director直接 根据不同类型的继承建造者实现类 调用就可以获得想要的对象
关键是这个Director 控制管理子类,将对象装配和构建解耦,使用不同的构造方法也就是构造器,来制定不同的装配方式;