建造者模式(学习笔记2021.09.16)
前言:
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
**意图:**将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
**主要解决:**主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
**何时使用:**一些基本部件不会变,而其组合经常变化的时候。
**如何解决:**将变与不变分离开。
**关键代码:**建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的 StringBuilder。
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
**注意事项:**与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
当一个类的构造函数参数个数超过4-5个,而且这些参数有些是可选的参数,考虑使用构造者模式。
前提条件
从上面可以看出, 假期这个
javaBean
有很多的参数, 并且有很多参数, 有的参数是必选的, 比如: 天数、旅馆、公园门票, 而又存在一些非必选的参数, 比如: 用餐、特殊活动、通常我们之前使用的解决方案是, 在javaBean中创建多个有参构造方法或者使用
set
方法,第一方案主要是使用及阅读不方便。你可以想象一下,当你要调用一个类的构造函数时,你首先要决定使用哪一个,然后里面又是一堆参数,如果这些参数的类型很多又都一样,你还要搞清楚这些参数的含义,很容易就传混了。
第二方案在构建过程中对象的状态容易发生变化,造成错误。因为那个类中的属性是分步设置的,所以就容易出错
为了解决这些痛点,builder模式就横空出世了。
使用建造者模式解决上面前提问题
简单模式的构造者
/**
* @Author: ZhiHao
* @Date: 2021/9/16 16:54
* @Description: 简单的建造者模式
* @Versions 1.0
**/
@ToString
public class Vacation {
private Integer day; // 天数, 必选
private String hotel; // 旅馆, 必选
private String parkTickets; // 公园门票, 必选
private HaveMeals haveMeals; // 吃饭, 可选
private Activity activity; // 活动, 可选
public Vacation(SimpleBuilder simpleBuilder) {
this.day = simpleBuilder.day;
this.hotel = simpleBuilder.hotel;
this.parkTickets = simpleBuilder.parkTickets;
this.haveMeals = simpleBuilder.haveMeals;
this.activity = simpleBuilder.activity;
}
// 内置静态简单生成器SimpleBuilder
public static class SimpleBuilder {
private Integer day; // 天数, 必选
private String hotel; // 旅馆, 必选
private String parkTickets; // 公园门票, 必选
private HaveMeals haveMeals; // 吃饭, 可选
private Activity activity; // 活动, 可选
public SimpleBuilder(Integer day, String hotel, String parkTickets) {
this.day = day;
this.hotel = hotel;
this.parkTickets = parkTickets;
}
// 可选的选择性设置
public SimpleBuilder setHaveMeals(HaveMeals haveMeals) {
this.haveMeals = haveMeals;
return this;
}
public SimpleBuilder setActivity(Activity activity) {
this.activity = activity;
return this;
}
// 最终建造出需要假期对象
public Vacation getVacation() {
return new Vacation(this);
}
}
}
活动与用餐类
@Data
public class Activity {
private String ActivityType;
public Activity(String activityType) {
ActivityType = activityType;
}
}
//-----------------------------------------------------------
@Data
public class HaveMeals {
private String restaurant;
public HaveMeals(String restaurant) {
this.restaurant = restaurant;
}
}
测试:
public void applicationTest() throws Exception {
Vacation.SimpleBuilder simpleBuilder = new Vacation.SimpleBuilder(1,"我的旅馆","100块的门票");
System.out.println(simpleBuilder.getVacation());
Vacation.SimpleBuilder simpleBuilder2 = new Vacation.SimpleBuilder(2,"国内旅馆","200块的门票");
Vacation vacation2 = simpleBuilder2.setHaveMeals(new HaveMeals("海鲜餐厅"))
.setActivity(new Activity("冰上模式"))
.getVacation();
System.out.println(vacation2);
Vacation.SimpleBuilder simpleBuilder3 = new Vacation.SimpleBuilder(3,"公园旅馆","230块的门票");
Vacation vacation3 = simpleBuilder3.setHaveMeals(new HaveMeals("自助餐厅"))
.setActivity(new Activity("末世竞技场"))
.getVacation();
System.out.println(vacation3);
}
// --------------------------------
Vacation(day=1, hotel=我的旅馆, parkTickets=100块的门票, haveMeals=null, activity=null)
Vacation(day=2, hotel=国内旅馆, parkTickets=200块的门票, haveMeals=HaveMeals(restaurant=海鲜餐厅), activity=Activity(ActivityType=冰上模式))
Vacation(day=3, hotel=公园旅馆, parkTickets=230块的门票, haveMeals=HaveMeals(restaurant=自助餐厅), activity=Activity(ActivityType=末世竞技场))
传统Builder
模式
传统builder
模式有4个角色。
- Product: 最终要生成的对象,例如
Vacation
实例。 - Builder: 构建者的抽象基类(有时会使用接口代替)。其定义了构建Product的抽象步骤,其实体类需要实现这些步骤。其会包含一个用来返回最终产品的方法
Product getProduct()
。 - ConcreteBuilder: Builder的实现类。
- Director: 决定如何构建最终产品的算法. 其会包含一个负责组装的方法
void Construct(Builder builder)
, 在这个方法中通过调用builder的方法,就可以设置builder,等设置完成后,就可以通过builder的getProduct()
方法获得最终的产品。
最终要生成的对象(角色)
我们最终需要生成的
Vacation
对象, 已经有了
@ToString
@Data
public class Vacation {
private Integer day; // 天数, 必选
private String hotel; // 旅馆, 必选
private String parkTickets; // 公园门票, 必选
private HaveMeals haveMeals; // 吃饭, 可选
private Activity activity; // 活动, 可选
public Vacation(Integer day, String hotel, String parkTickets) {
this.day = day;
this.hotel = hotel;
this.parkTickets = parkTickets;
}
}
创建构建者的抽象基类(角色)
public abstract class VacationBuilder {
// 添加餐厅
public abstract VacationBuilder addHaveMeals();
// 添加活动
public abstract VacationBuilder addActivity();
// 获取假期对象
public abstract Vacation getVacation();
}
构建者的实现类(角色)
/**
* @Author: ZhiHao
* @Date: 2021/9/16 18:43
* @Description: 构造第一天假期实现类
* @Versions 1.0
**/
public class OneDayVacationBuilderImpl extends VacationBuilder {
private Vacation vacation;
public OneDayVacationBuilderImpl(Integer day,
String hotel,
String parkTickets) {
this.vacation = new Vacation(day,hotel,parkTickets);
}
@Override
public VacationBuilder addHaveMeals() {
return this;
}
@Override
public VacationBuilder addActivity() {
return this;
}
@Override
public Vacation getVacation() {
return vacation;
}
}
// -----------------------------第二天假期----------------------------------
/**
* @Author: ZhiHao
* @Date: 2021/9/16 18:43
* @Description: 构造第二天假期实现类
* @Versions 1.0
**/
public class TwoDayVacationBuilderImpl extends VacationBuilder {
private Vacation vacation;
public TwoDayVacationBuilderImpl(Integer day,
String hotel,
String parkTickets) {
this.vacation = new Vacation(day,hotel,parkTickets);
}
@Override
public VacationBuilder addHaveMeals() {
vacation.setHaveMeals(new HaveMeals("海鲜餐厅"));
return this;
}
@Override
public VacationBuilder addActivity() {
vacation.setActivity(new Activity("冰上模式"));
return this;
}
@Override
public Vacation getVacation() {
return vacation;
}
}
指挥者 (角色)
构建的算法调用者, 调用构造者, 进行构造并最终获取到构造出来的对象。
/**
* @Author: ZhiHao
* @Date: 2021/9/16 18:52
* @Description: 指挥者, 负责调用建造者, 都是单一职责
* @Versions 1.0
**/
public class VacationDirector {
private VacationBuilder vacationBuilder;
public VacationDirector(VacationBuilder vacationBuilder) {
this.vacationBuilder = vacationBuilder;
}
// 进行建造
public Vacation ConstructVacation() {
return vacationBuilder.addActivity()
.addHaveMeals()
.getVacation();
}
}
测试
public void applicationTest() throws Exception {
VacationBuilder vacationBuilder = new OneDayVacationBuilderImpl(1,"我的旅馆","100块的门票");
VacationDirector vacationDirector = new VacationDirector(vacationBuilder);
System.out.println(vacationDirector.constructVacation());
VacationBuilder vacationBuilder2 = new TwoDayVacationBuilderImpl(2,"旅馆","50块的门票");
VacationDirector vacationDirector2 = new VacationDirector(vacationBuilder2);
System.out.println(vacationDirector2.constructVacation());
}
// ----------------------------------------------
Vacation(day=1, hotel=我的旅馆, parkTickets=100块的门票, haveMeals=null, activity=null)
Vacation(day=2, hotel=旅馆, parkTickets=50块的门票, haveMeals=HaveMeals(restaurant=海鲜餐厅), activity=Activity(ActivityType=冰上模式))
最后如果不遵守单一职责的时候, 可以把指挥者 (角色) 放到建造者抽象基类中
public abstract class VacationBuilder {
// 添加餐厅
public abstract VacationBuilder addHaveMeals();
// .......
public Vacation constructVacation(VacationBuilder vacationBuilder) {
return vacationBuilder.addActivity()
.addHaveMeals()
.getVacation();
}
}
1