概念
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
即将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
该模式有下列几个角色:
Builder (**抽象建造者):为创建一个产品对象的各个部件指定抽象接口**。
ConcreteBuilder (**具体建造者):实现Builder的接口**以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。
Director (指挥者):构造一个使用Builder接口的对象。调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
Product (**产品):被构造的复杂对象**。
推演
需求**:KFC购买食物套餐,主要包括主食(StapleFood)、小吃(Snack)、饮料(Drink)三样**
反例(传统套路)
创建购买对象Meal
@Getter@Setter@ToStringclass Meal{/*** 主食*/private String stapleFood;/*** 小吃*/private String snack;/*** 饮料*/private String drink;}
小明和小李购买组合如下:
Meal meal = new Meal();meal.setStapleFood("无敌汉堡包");meal.setSnack("炸薯条");meal.setDrink("阔落");System.out.println(meal);
结果:
Meal(stapleFood=无敌汉堡包, snack=炸薯条, drink=阔落)
问题:也就是说,我是顾客,去购买一个套餐还要自己负责食物组合,食物装填这些细节工作,实在是太麻烦,我知道的太多了(违反了迪米特法则)
正例1
针对上一问题,专门创建一个“MealBuilder” 建造者类,这个类负责封装装填套餐的过程(即创建套餐的过程)
class MealBuilder{private Meal meal = new Meal();public Meal build(){meal.setStapleFood("无敌汉堡包");meal.setSnack("炸薯条");meal.setDrink("阔落");return meal;}}
此时再去购买套餐便是如下:
Meal meal = new MealBuilder().build();System.out.println(meal);
结果:
Meal(stapleFood=无敌汉堡包, snack=炸薯条, drink=阔落)
这种写法的优点是:客户端无需知道目标产品的细节,只需要调用建造者即可,建造者封装了创建产品的复杂过程
问题**:封装太死,只有一种套餐,无法满足变化需求
正例2
针对上述问题,我们可以创建多个不同的建造者,用于生产不同细节的产品,如下:
/*** 无敌套餐-建造者*/class InvincibleMealBuilder{private Meal meal = new Meal();public Meal build(){meal.setStapleFood("无敌汉堡包");meal.setSnack("炸薯条");meal.setDrink("阔落");return meal;}}/*** 巨无霸套餐-建造者*/class BigMacMealBuilder{private Meal meal = new Meal();public Meal build(){meal.setStapleFood("巨无霸汉堡包");meal.setSnack("香芋丸子");meal.setDrink("咖啡");return meal;}}
此时再去购买套餐便是如下:
//给小胖来个巨无霸套餐Meal bmm = new BigMacMealBuilder().build();System.out.println(bmm);//给小明来个无敌套餐Meal im = new InvincibleMealBuilder().build();System.out.println(im);
结果:
Meal(stapleFood=巨无霸汉堡包, snack=香芋丸子, drink=咖啡)Meal(stapleFood=无敌汉堡包, snack=炸薯条, drink=阔落)
优点:满足了不同套餐的需求
缺点**:1、建造者频繁出现重复代码
2、建造过程不稳定,如果某个建造者没有执行某个过程(如未装填薯条/阔落),得到的产品将会出现问题。**
正例3
为了解决上述问题,我们将为建造者创造一份规范(抽象),建造者按照规定流程执行,使建造者的建造过程稳定下来
/*** 建造者抽象*/interface Builder{void setStapleFood();void setSnack();void setDrink();Meal build();}
建造者们直接实现上述接口:
/*** 无敌套餐-建造者*/class InvincibleMealBuilder implements Builder{private Meal meal = new Meal();@Overridepublic void setStapleFood() {meal.setStapleFood("无敌汉堡包");}@Overridepublic void setSnack() {meal.setSnack("炸薯条");}@Overridepublic void setDrink() {meal.setDrink("阔落");}@Overridepublic Meal build(){return meal;}}/*** 巨无霸套餐-建造者*/class BigMacMealBuilder implements Builder{private Meal meal = new Meal();@Overridepublic void setStapleFood() {meal.setStapleFood("巨无霸汉堡包");}@Overridepublic void setSnack() {meal.setSnack("香芋丸子");}@Overridepublic void setDrink() {meal.setDrink("咖啡");}@Overridepublic Meal build(){return meal;}}
此时再去购买套餐便是如下:
//给小胖来个巨无霸套餐Builder bmmBuilder = new BigMacMealBuilder();bmmBuilder.setStapleFood();bmmBuilder.setSnack();bmmBuilder.setDrink();Meal bmm =bmmBuilder.build();System.out.println(bmm);//给小明来个无敌套餐Builder imBuilder = new InvincibleMealBuilder();imBuilder.setStapleFood();imBuilder.setSnack();imBuilder.setDrink();Meal im = imBuilder.build();System.out.println(im);
优点:建造者的建造过程是稳定的,不会漏掉中间的某一步。
缺点:现在又变成了客户端组合套餐,违反了迪米特法则,现在相当于客户端指挥建造者的建造步骤去创建产品
正例4(真正的建造者模式)
创建一个指挥者角色,用于指挥建造者,即将上述指挥建造者的代码进行封装
/*** 指挥者*/class MealDirect{public Meal build(Builder builder){builder.setStapleFood();builder.setSnack();builder.setDrink();return builder.build();}}
此时再去购买套餐便是如下:
//给小胖来个巨无霸套餐MealDirect direct = new MealDirect();Builder bmmBuilder = new BigMacMealBuilder();Meal bmm =direct.build(bmmBuilder);System.out.println(bmm);//给小明来个无敌套餐Builder imBuilder = new InvincibleMealBuilder();Meal im = direct.build(imBuilder);System.out.println(im);
** 如此便是真正的建造者模式,此时如果需要扩展一个建造者,仅仅需要创建一个建造者并将其传入direct.build()方法即可,无需修改源码,符合开闭原则**
思维推演
|——传统套路:客户端完全知道细节,自行组装套餐,违反迪米特法则
|
|——新增建造者,封装组装过程,客户端仅需要调用建造者提供的接口即可,但是封装过死,需求无法变动
|
|——创建不同的建造者,满足变化需求,但是建造者的建造过程不稳定,既建造步骤不规范,可能存在缺失
|
|——创建一个建造者抽象,规范化建造者建造过程,使建造过程稳定下来,但是此时完全由客户端指挥建造者建造产品的步骤,又违反了迪米特法则
|
|——创建一个指挥者对象,封装建造者的建造步骤调用,客户端仅需要调用指挥者去指挥建造者即可,建造者的建造细节对客户端完全封闭,符合迪米特法则和开闭原则
应用场景
创造的产品较为复杂,由多个成员组成,且成员面临着剧烈的变化,但成员间的建造顺序是稳定的
创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的。
优点
简化代码及封闭构建对象的细节,让客户端的调用更简洁明了,提高代码可读性
缺点
**
同工厂模式一样,一旦产生多种产品,将会产生多个建造者,增加代码量
建造者模式与工厂模式的区别
工厂模式关注的角度是产品的创建
而建造者模式关注的角度是产品创建后的属性赋值
如有贻误,还请评论指正**
