1. 什么是生成器模式
Builder模式也叫建造者模式或者生成器模式,是由GoF提出的23种设计模式中的一种。Builder模式是一种对象创建型模式之一,用来隐藏复合对象的创建过程,它把复合对象的创建过程加以抽象,通过子类继承和重载的方式,动态地创建具有复合属性的对象。
简单来说就是封装一个复杂对象的构造过程,并允许按照步骤构造。
生成器模式的结构:
- 对象的创建:Builder模式是为对象的创建而设计的模式
- 创建的是一个复合对象:被创建的对象为一个具有复合属性的复合对象
- 关注对象创建的各部分的创建过程:不同的工厂(这里指builder生成器)对产品属性有不同的创建方法
适用场景:
- 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
- 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
- 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
优缺点:
优点:
-
封装性很好:使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在导演类中对整体而言可以取得比较好的稳定性。
-
扩展性很好:建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险。
-
有效控制细节风险:由于具体的建造者是独立的,因此可以对建造者过程逐步细化,而不对其他的模块产生任何影响。
-
将复杂对象的创建过程封装起来
-
允许对象通过几个步骤来创建,并且可以改变过程(工厂模式只有一个步骤)
-
只需指定具体生成器就能生成特定对象,隐藏类的内部结构
对象的实现可以被替换
缺点:
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
2. 具体实例
这里举一个具体的例子来介绍生成器模式是怎么使用的.
度假计划生成项目:就是一个公司要推出度假的活动,然后会有具体的安排计划。
度假计划的因素:时间、门票、餐厅、住宿、特殊活动等。
所以我们就像抽象一个度假类,然后每一天去继承和扩展这个类。所以类图如下:
这样一个计划就是一个具体对象,便于扩展。
然后我们基于上面的基础,我们想用户可以自己选择具体是怎么过?选什么样的方式,就是我选3天的度假模式,还是选四天的度假模式,或者是其他种类的。这样的话我们就需要在设计一个抽象类AbsBuilder的类,然后在这个类中实现Vaction,然后用户通过Director就可以自行选择具体是什么样的度假模式,不用管底层的实现,直接使用这个类就可以了。然后要添加新的度假类的时候也不需要修改,符合开闭原则。具体的类图如下:
具体的代码实现:
Vacation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | 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 setStDate(String std) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try { mStDate = sdf.parse(std); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public Date getStDate() { return mStDate; } public void addDay() { mVacationDay = new VacationDay(nextDate(mDays)); mVacationDayLst.add(mVacationDay); mDays++; } public boolean setVacationDay(int i) { if ((i > 0) && (i < mVacationDayLst.size())) { mVacationDay = mVacationDayLst.get(i); return true; } mVacationDay = null; return false; } 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(); } } |
VacationDay:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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 setDate(Date date) { mDate = date; } 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(); } } |
抽象类AbsBuilder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public abstract class AbsBuilder { public Vacation mVacation; public AbsBuilder(String std) { mVacation = new Vacation(std); } public abstract void buildvacation(); public abstract void buildDay(int i); public abstract void addHotel(String hotel); public abstract void addTicket(String ticket); public abstract void addEvent(String tvent); public Vacation getVacation() { return mVacation; } } |
具体的度假模式(3天):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | public class Builder3d extends AbsBuilder { public Builder3d(String std) { super(std); // TODO Auto-generated constructor stub } @Override public void buildDay(int i) { // TODO Auto-generated method stub mVacation.setVacationDay(i); } @Override public void addHotel(String hotel) { // TODO Auto-generated method stub mVacation.setHotel(hotel); } @Override public void addTicket(String ticket) { // TODO Auto-generated method stub mVacation.addTicket(ticket); } @Override public void addEvent(String event) { // TODO Auto-generated method stub mVacation.addEvent(event); } @Override public void buildvacation() { // TODO Auto-generated method stub 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"); addHotel("Four Seasons"); mVacation.addDay(); addTicket("Plane Ticket"); addEvent("City Tour"); addEvent("Fly to Home"); } } |
4天的模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | public class Builder4d extends AbsBuilder { public Builder4d(String std) { super(std); // TODO Auto-generated constructor stub } @Override public void buildDay(int i) { // TODO Auto-generated method stub mVacation.setVacationDay(i); } @Override public void addHotel(String hotel) { // TODO Auto-generated method stub mVacation.setHotel(hotel); } @Override public void addTicket(String ticket) { // TODO Auto-generated method stub mVacation.addTicket(ticket); } @Override public void addEvent(String event) { // TODO Auto-generated method stub mVacation.addEvent(event); } @Override public void buildvacation() { // TODO Auto-generated method stub addTicket("Plane Ticket"); addEvent("Fly to Destination"); addEvent("Supper"); addHotel("Hilton"); mVacation.addDay(); addTicket("Zoo Ticket"); addEvent("Bus to Zoo"); addEvent("Feed animals"); addHotel("Hilton"); mVacation.addDay(); addTicket("Beach"); addEvent("Swimming"); addHotel("Home inn"); mVacation.addDay(); addTicket("Plane Ticket"); addEvent("Fly to Home"); } } |
Director:在Director中传入Builder,然后构造的时候实现vacation的实例化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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(); } } |
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public class MainTest { public static void main(String[] args) { Director mDirector = new Director(new Builder4d("2015-12-29")); mDirector.construct(); mDirector.setBuilder(new Builder3d("2015-8-30")); mDirector.construct(); testself() ; } public static void testself() { BuilderSelf builder = new BuilderSelf("2015-9-29"); builder.addTicket("Plane Ticket").addEvent("Fly to Destination") .addEvent("Supper").addHotel("Hilton"); builder.addDay().addTicket("Zoo Ticket").addEvent("Bus to Zoo") .addEvent("Feed animals").addHotel("Home Inn"); builder.addDay(); builder.addTicket("Beach"); builder.addEvent("Swimming"); builder.addHotel("Home inn"); builder.addDay().addTicket("Plane Ticket").addEvent("Fly to Home"); builder.getVacation().showInfo(); } } |
3. 生成器模式与抽象工厂的差异
生成器模式和抽象工厂模式在功能上很相似,主要区别:
- 生成器一般用来创建大的复杂的对象
- 生成器模式强调的是一步步创建对象,可以改变步骤来生成不同的对象
- 一般来说生成器模式中对象不直接返回