生成器模式
生成器模式(Builder Pattern)又叫创建者模式、建造者模式等等,使用生成器封装一个产品的构造过程,并允许按步骤构造。
假设我们有一下场景:
现在有一个主题公园,请你为他们制定一套度假计划:客人可以选择旅馆以及各种门票、餐厅订位,甚至也可以选择登记参加特殊的活动,首先你需要建立了像下面这样的结构:
从上面可以看出每个假期都有很几天,其实主要专注一天的设计就足够了,其他的都是重复的工作。
你需要一个有弹性的设计
每个客人的度假计划都不太一样,例如天数、活动类型。比如,当地居民可以不需要旅馆,但是想要用餐并参加特殊活动。而其他的客人可能是从外地飞过来的,所以需要旅馆、用餐和门票。
所以你需要一个有弹性的数据结构,代表客人的规划,以及所有的变化;你也需要遵循一系列潜在的复杂顺序,创建这样的规划。你要如何才能够提供一种方式 来创建这个复杂的结构,而不会和创建它的步骤混在一起呢?
使用生成器模式
我们将旅客规划的过程,封装到一个对象中(让我们称此对象为生成器),然后客户调用生产器为它创建旅游规划,UML如图所示:
客户端按照生成器生成所有的对象,然后可以通过调用planner.getPlannerDes()获取相应的计划描述,简易代码如下所示:
//建造者抽象类
public interface AbstractBuilder{
void buildDay(int day);
void addHotel(boolean hotel);
void addReservation(boolean reservation);
void addSpecialEvent(List<String> specialEvent);
void addTickets(int tickets);
Vacation getVacation();
}
//假期建造者实现类
public class VacationBuilder implements AbstractBuilder{
Vacation vacation;
public VacationBuilder() {
this.vacation = new Vacation();
}
@Override
public void buildDay(int day) {
vacation.setBuildDay(day);
}
@Override
public void addHotel(boolean hotel) {
vacation.setHotel(hotel);
}
@Override
public void addReservation(boolean reservation) {
vacation.setReservation(reservation);
}
@Override
public void addSpecialEvent(List<String> specialEvent) {
vacation.setSpecialEvents(specialEvent);
}
@Override
public void addTickets(int tickets) {
vacation.setTickets(tickets);
}
@Override
public Vacation getVacation() {
return vacation;
}
}
//一天的安排Vacation类
public class Vacation {
private Integer buildDay;
private boolean hotel;
private Integer tickets;
private Boolean reservation;
private List<String> specialEvents;
//getter setter省略
}
//整个计划类
public class Planner {
List<Vacation> vacationList;
public Planner() {
this.vacationList = new ArrayList<>();
}
public String getPlannerDes(){
StringBuilder sb = new StringBuilder();
sb.append("尊敬的顾客,你的行程安排如下:\n");
for (int i=0;i<vacationList.size();i++){
Vacation vacation = vacationList.get(i);
sb.append("第")
.append(vacation.getBuildDay())
.append("天行程:")
.append("是否入住酒店:")
.append(vacation.isHotel())
.append(" 是否需要就餐:")
.append(vacation.getReservation())
.append(" 特殊活动:");
if (vacation.getSpecialEvents()!=null){
sb.append(vacation.getSpecialEvents().toString());
}else{
sb.append("无");
}
sb.append(" 购买门票数:").append(vacation.getTickets()).append("\n");
}
return sb.toString();
}
}
//client类及其测试
public class Cilent {
public Planner constructVavation(int vacationDay,HashMap<String,Boolean> trueOrNot,List<List<String>> specialEvents,List<Integer>tickets){
Planner planner = new Planner();
for(int i=0;i<vacationDay;i++){
int index = i+1;
VacationBuilder vacationBuilder = new VacationBuilder();
vacationBuilder.buildDay(index);
vacationBuilder.addHotel(trueOrNot.get("hotel"+index));
vacationBuilder.addReservation(trueOrNot.get("reservation"+index));
vacationBuilder.addSpecialEvent(specialEvents.get(i));
vacationBuilder.addTickets(tickets.get(i));
planner.vacationList.add(vacationBuilder.getVacation());
}
return planner;
}
public static void main(String[] args) {
int vacationDay = 3;
HashMap<String,Boolean> trueOrNot = new HashMap<>();
trueOrNot.put("hotel1",true);
trueOrNot.put("hotel2",true);
trueOrNot.put("hotel3",false);
trueOrNot.put("reservation1",false);
trueOrNot.put("reservation2",false);
trueOrNot.put("reservation3",true);
List<Integer> tickets = new ArrayList<>();
tickets.add(10);
tickets.add(20);
tickets.add(30);
List<List<String>> specialEvents = new ArrayList<>();
specialEvents.add(null);
specialEvents.add(null);
List<String> events = new ArrayList<>();
events.add("浴足");
events.add("看电影");
specialEvents.add(events);
Planner planner = new Cilent().constructVavation(vacationDay, trueOrNot, specialEvents, tickets);
System.out.println(planner.getPlannerDes());
}
}
//执行结果
//尊敬的顾客,你的行程安排如下:
//第1天行程:是否入住酒店:true 是否需要就餐:false 特殊活动:无 购买门票数:10
//第2天行程:是否入住酒店:true 是否需要就餐:false 特殊活动:无 购买门票数:20
//第3天行程:是否入住酒店:false 是否需要就餐:true 特殊活动:[浴足, 看电影] 购买门票数:30
这样客户端就可以通过直接调用constructVavation来创建对象而不需要关系过程。
优缺点及用途
优点
- 将一个复杂的对象的创建封装起来。
- 允许对象通过多个步骤来创建,并且可以改变过程(这与工厂模式只有一个步骤是不同的)。
- 向客户隐藏产品的内部表现。
- 产品的实现可以被替换,因为客户只看到一个抽象的接口。
缺点
与工厂模式相比,采用生成器创建对象的客户需要了解更多的知识。
用途
经常被用在创建组合结构。
注:生产器本人也没有琢磨地很明白,欢迎大家的指导。