生成器模式(Builder Pattern)
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
将一个复杂对象的构建与它的表示分离,使同样的构建过程可以创建不同的表示。
生成器模式:封装一个复杂对象构造过程,并允许按步骤构造。
何时使用:
- 当系统准备为用户提供一个内部结构复杂的对象,而且在构造方法中编写创建该对象的代码无法满足用户需求时,就可以使用生成器模式老构造这样的对象。
- 当某些系统要求对象的构造过程必须独立于创建该对象的类时。
优点:
- 生成器模式将对象的构造过程封装在具体的生成器中,用户使用不同的具体生成器就可以得到该对象的不同表示。
- 生成器模式将对象的构造过程从创建该对象的类中分离出来,使用户无须了解该对象的具体组件。
- 可以更加精细有效的控制对象的构造过程。将对象的构造过程分解成若干步骤,这就是程序可以更加精细,有效的控制整个对象的构造。
- 生成器模式将对象的构造过程与创建该对象类解耦,是对象的创建更加灵活有弹性。
- 当增加新的具体的生成器是,不必修改指挥者的代码,即该模式满足开-闭原则。
模式的重心在于分离构建算法和具体的构造实现,从而使构建算法可以重用。
比如我们要得到一个日期,可以有不同的格式,然后我们就使用不同的生成器来实现。
优点:
将复杂对象的创建过程封装起来
允许对象通过几个步骤来创建,并且可以改变过程(工厂模式只有一个步骤)
只需指定具体生成器就能生成特定对象,隐藏类的内部结构
对象的实现可以被替换
生成器模式和抽象工厂模式在功能上很相似,主要区别:
生成器一般用来创建大的复杂的对象
生成器模式强调的是一步步创建对象,可以改变步骤来生成不同的对象
一般来说生成器模式中对象不直接返回
首先是这个类(产品):
public class MyDate {
public String date;
}
然后就是抽象生成器,描述生成器的行为:
public interface IDateBuilder {
IDateBuilder buildDate(int y,int m,int d);
String date();
}
接下来是具体生成器,一个以“-”分割年月日,另一个使用空格:
//具体生成器
public class DateBuilder1 implements IDateBuilder {
private MyDate myDate;
public DateBuilder1(MyDate myDate) {
this.myDate = myDate;
}
@Override
public IDateBuilder buildDate(int y, int m, int d) {
myDate.date = y + "-" + m + "-" + d;
return this;
}
@Override
public String date() {
return myDate.date;
}
}
public class DateBuilder2 implements IDateBuilder {
private MyDate myDate;
public DateBuilder2(MyDate myDate) {
this.myDate = myDate;
}
@Override
public IDateBuilder buildDate(int y, int m, int d) {
myDate.date = y + " " + m + " " + d;
return this;
}
@Override
public String date() {
return myDate.date;
}
}
接下来是指挥官,向用户提供具体的生成器:
//指挥者
public class Derector {
private IDateBuilder builder;
public Derector(IDateBuilder builder) {
this.builder = builder;
}
public String getDate(int y, int m, int d) {
builder.buildDate(y, m, d);
return builder.date();
}
}
使用如下:
public class TestUse {
public static void main(String[] args) {
MyDate date = new MyDate();
IDateBuilder builder;
builder = new DateBuilder1(date).buildDate(2066, 3, 5);
System.out.println(builder.date());
builder = new DateBuilder2(date).buildDate(2066, 3, 5);
System.out.println(builder.date());
Derector dc = new Derector(new DateBuilder1(date));
System.out.println(dc.getDate(2066, 3, 5));
}
}
使用不同生成器,可以使原有产品表现得有点不一样。
往往在实际的应用中,生成器要做的工作不会这么简单,而是相对复杂的(因为其产品一般是比较复杂的),原有构建的维护会转移到生成器的维护上。
builder 设计模式的使用场景
1.基本对象
@Data
@ToString
public class User {
private String firstName; // required
private String lastName; // required
private int age; // optional
private String phone; // optional
private String address; // optional
public User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName=builder.lastName ;
this.age=builder.age;
this.phone=builder.phone;
this.address=builder.address;
}
public static class UserBuilder{
private final String firstName; // required
private String lastName; // required
private int age; // optional
private String phone; // optional
private String address; // optional
public UserBuilder(String firstName) {
super();
this.firstName = firstName;
}
public UserBuilder excuteLastName(String lastName){
this.lastName = lastName;
return this;
}
public UserBuilder excuteAge(int age){
this.age = age;
return this;
}
public UserBuilder excutePhone(String phone){
this.phone = phone;
return this;
}
public UserBuilder excuteAddress(String address){
this.address = address;
return this;
}
public User getUser(){
return new User(this);
}
}
}
测试:
public User getUser(){
return new User.UserBuilder("aa").excuteAddress("addr")
.excuteAge(12).excutePhone("123").getUser();
}
2.Map 的进一步封装,使得支持链式
public class SelfMap{
private Map<String,Object> map = new HashMap<String,Object>();
public SelfMap() {
}
public SelfMap addVal(String key,Object value){
map.put(key, value);
return this;
}
public SelfMap removeVal(String key){
if(map.keySet().contains(key)){
map.remove(key);
}
return this;
}
public Object getVal(String key){
if(map.keySet().contains(key)){
return map.get(key);
}
return null;
}
public Map<String, Object> builder() {
return map;
}
}
测试:
SelfMap map = new SelfMap().addVal("aa", "123456")
.addVal("bb", "56");
map.addVal("f", "44").removeVal("dfg").removeVal("aa");
Object obj = map.getVal("bb");
System.out.println(obj);
3.综合实例
public class VacationDay {
private Date mDate;
private String mHotels;
private ArrayList<String> mTickets = null;
private ArrayList<String> mEvents = null;
public VacationDay(Date date) {
mDate = date;
mTickets = new ArrayList<String>();
mEvents = new ArrayList<String>();
}
public void setHotel(String mHotels) {
this.mHotels = mHotels;
}
public void addTicket(String ticket) {
mTickets.add(ticket);
}
public void addEvent(String event) {
mEvents.add(event);
}
public String showInfo() {
StringBuilder stb = new StringBuilder();
stb.append("Date:" + mDate.toString() + "\n");
stb.append("Hotel:" + mHotels + "\n");
stb.append("Tickets:" + mTickets.toString() + "\n");
stb.append("Events" + mEvents.toString() + "\n");
return stb.toString();
}
}
public class Vacation {
private ArrayList<VacationDay> mVacationDayLst;
private Date mStDate;
private int mDays = 0;
private VacationDay mVacationDay;
public Vacation(String std) {
mVacationDayLst = new ArrayList<VacationDay>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
mStDate = sdf.parse(std);
mVacationDay = new VacationDay(mStDate);
mVacationDayLst.add(mVacationDay);
mDays++;
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void addDay() {
mVacationDay = new VacationDay(nextDate(mDays));
mVacationDayLst.add(mVacationDay);
mDays++;
}
public void setHotel(String mHotels) {
mVacationDay.setHotel(mHotels);
}
public void addTicket(String ticket) {
mVacationDay.addTicket(ticket);
}
public void addEvent(String event) {
mVacationDay.addEvent(event);
}
public void showInfo() {
for (int i = 0, len = mVacationDayLst.size(); i < len; i++) {
System.out.println("** " + (i + 1) + " day**");
System.out.println(mVacationDayLst.get(i).showInfo());
}
}
private Date nextDate(int n) {
Calendar cal = Calendar.getInstance();
cal.setTime(mStDate);
cal.add(Calendar.DATE, n);
return cal.getTime();
}
}
builder 实现
public abstract class AbsBuilder {
public Vacation mVacation;
public AbsBuilder(String std) {
mVacation = new Vacation(std);
}
public abstract void buildvacation();
public abstract void addHotel(String hotel);
public abstract void addTicket(String ticket);
public abstract void addEvent(String tvent);
public Vacation getVacation() {
return mVacation;
}
}
public class Builder3d extends AbsBuilder {
public Builder3d(String std) {
super(std);
}
@Override
public void addHotel(String hotel) {
mVacation.setHotel(hotel);
}
@Override
public void addTicket(String ticket) {
mVacation.addTicket(ticket);
}
@Override
public void addEvent(String event) {
mVacation.addEvent(event);
}
@Override
public void buildvacation() {
addTicket("Plane Ticket");
addEvent("Fly to Destination");
addEvent("Supper");
addEvent("Dancing");
addHotel("Four Seasons");
mVacation.addDay();
addTicket("Theme Park");
addEvent("Bus to Park");
addEvent("lunch");
addTicket("Theme Park qq");
addHotel("Four Seasons");
mVacation.addDay();
addTicket("Plane Ticket");
addEvent("City Tour");
addEvent("Fly to Home");
}
}
Director 实现
public class Director {
private AbsBuilder builder;
public Director(AbsBuilder builder)
{
this.builder=builder;
}
public void setBuilder(AbsBuilder builder)
{
this.builder=builder;
}
public void construct()
{
builder.buildvacation();
builder.getVacation().showInfo();
}
}
测试:
Director mDirector = new Director(new Builder4d("2015-12-29"));
mDirector.construct();
可以改造一下:
public class BuilderSelf {
public Vacation mVacation;
public BuilderSelf(String std) {
mVacation = new Vacation(std);
}
public BuilderSelf addDay() {
mVacation.addDay();
return this;
}
public BuilderSelf addHotel(String hotel) {
mVacation.setHotel(hotel);
return this;
}
public BuilderSelf addTicket(String ticket) {
mVacation.addTicket(ticket);
return this;
}
public BuilderSelf addEvent(String event) {
mVacation.addEvent(event);
return this;
}
public Vacation getVacation() {
return mVacation;
}
}