4.1关联背景故事
去营业厅办理手机套餐,有以下两种套餐可以选择:
1)20元包400条短信的套餐;
2)30元包600条短信的套餐。
并且两种套餐都必须开通彩铃业务。
4.2模式定义
建造者模式(Builder Pattern),在一个软件系统中,可能会面临创建一些复杂对象的工作,如果我们使用单一的方法或单一的对象来创建会比较繁琐,当所创建的复杂对象发生变化时,整个系统就可能面临剧烈的变化。
为了解决这个问题,我们可以将这个复杂对象的创建过程分解成若干个部分,各个子部分用一定的算法构成。但是,子部分可能会经常发生改变,如何能保证整体创建工作的稳定性呢?这就需要建造者模式的支持了。建造者模式把复杂对象的创建与表示分离,使得同样的构建过程可以创建不同的表示。
4.3故事中的模式分析
4.3.1角色分析
例子中出现的事物对象有:
1)客户张三
2)营业厅操作员
3)设置客户套餐的计算机
4)手机套餐
客户张三就是最终获得手机套餐的终端,营业厅操作员相当于一个指导者的身份,只有操作员才知道客户需要什么样的套餐,而计算机充当着一个建造者的角色,计算机通过操作设置客户的手机套餐,手机套餐就是最终的产品,生产出来给客户使用。
例子中的各种角色列出如下:
客户张三——终端需求
营业厅操作员——指导者(通知指导建造者生产什么样的手机套餐)
计算机——建造者(建造各种手机套餐)
手机套餐——产品
4.3.2建造者模式的静态建模
如图所示,在建造者工厂中将构建手机套餐对象细化分解成三个过程:构建话费、构建短信、构建彩铃。每一个过程实现的功能很专业化、单一化,三个功能又互不干扰,还有一个功能返回手机套餐实例,使构建和表示分离,功能模块之间的耦合度大大降低,模块内部具有很高的内聚度。
4.4模式实现
4.4.1创建产品——手机套餐
package com.demo.builder.model; /** * Created by lsq on 2018/3/14. * 手机套餐类 */ public class MobilePackage { //话费 private float money; //短信 private int shortInfo; //彩铃 private String music; public float getMoney() { return money; } public void setMoney(float money) { this.money = money; } public int getShortInfo() { return shortInfo; } public void setShortInfo(int shortInfo) { this.shortInfo = shortInfo; } public String getMusic() { return music; } public void setMusic(String music) { this.music = music; } }
4.4.2建立抽象建造者
1)建造者接口——IMobileBuilder
package com.demo.builder.itf; import com.demo.builder.model.MobilePackage; /** * Created by lsq on 2018/3/14. * 手机套餐建造者 */ public interface IMobileBuilder { //建造手机套餐的话费 public void buildMoney(); //建造手机套餐的短信 public void buildShortInfo(); //建造手机套餐的彩铃 public void buildMusic(); //返回建造的手机套餐对象 public MobilePackage getMobilePackage(); }
2)抽象建造者——AbstractBasePackage
package com.demo.builder.base; import com.demo.builder.model.MobilePackage; /** * Created by lsq on 2018/3/14. * 抽象建造者 */ public abstract class AbstractBasePackage { //手机套餐实例变量 protected MobilePackage mobilePackage; public AbstractBasePackage() { this.mobilePackage = new MobilePackage(); } }
4.4.3建立具体建造者
1)具体的建造者——MobileBuilderImpl1
package com.demo.builder.itf; import com.demo.builder.base.AbstractBasePackage; import com.demo.builder.model.MobilePackage; /** * Created by lsq on 2018/3/14. * 套餐1 */ public class MobileBuilderImpl1 extends AbstractBasePackage implements IMobileBuilder { //建造手机套餐的话费 public void buildMoney() { this.mobilePackage.setMoney(20.0f); } //建造手机套餐的短信 public void buildShortInfo() { this.mobilePackage.setShortInfo(400); } //建造手机套餐的彩铃 public void buildMusic() { this.mobilePackage.setMusic("我们都一样"); } //返回建造的手机套餐对象 public MobilePackage getMobilePackage() { return this.mobilePackage; } }
2)具体建造者——MobileBuilderImpl2
package com.demo.builder.itf; import com.demo.builder.base.AbstractBasePackage; import com.demo.builder.model.MobilePackage; /** * Created by lsq on 2018/3/14. * 套餐2 */ public class MobileBuilderImpl2 extends AbstractBasePackage implements IMobileBuilder { //建造手机套餐的话费 public void buildMoney() { this.mobilePackage.setMoney(30.0f); } //建造手机套餐的短信 public void buildShortInfo() { this.mobilePackage.setShortInfo(600); } //建造手机套餐的彩铃 public void buildMusic() { this.mobilePackage.setMusic("我们不一样"); } //返回建造的手机套餐对象 public MobilePackage getMobilePackage() { return this.mobilePackage; } }
4.4.4创建指导者
指导者类含有一个方法,该方法以建造者实例对象作为参数,设置客户话费、短信和彩铃信息,然后返回手机套餐实例。
package com.demo.builder.director; import com.demo.builder.itf.IMobileBuilder; import com.demo.builder.model.MobilePackage; /** * Created by lsq on 2018/3/14. * 手机套餐指导者 */ public class MobileDirector { public MobilePackage createMobilePackage(IMobileBuilder mobileBuilder){ if (mobileBuilder != null){ //构建话费 mobileBuilder.buildMoney(); //构建短信 mobileBuilder.buildShortInfo(); //构建彩铃 mobileBuilder.buildMusic(); //返回手机套餐 return mobileBuilder.getMobilePackage(); } return null; } }
4.4.5客户订购手机套餐
import com.demo.builder.director.MobileDirector; import com.demo.builder.itf.MobileBuilderImpl1; import com.demo.builder.itf.MobileBuilderImpl2; import com.demo.builder.model.MobilePackage; /** * Created by lsq on 2018/3/13. * */ public class MainApp { public static void main(String[] args) { //创建指导者 MobileDirector mobileDirector = new MobileDirector(); //套餐1 MobileBuilderImpl1 mobileBuilderImpl1 = new MobileBuilderImpl1(); //套餐2 MobileBuilderImpl2 mobileBuilderImpl2 = new MobileBuilderImpl2(); printMessage(mobileDirector.createMobilePackage(mobileBuilderImpl1)); printMessage(mobileDirector.createMobilePackage(mobileBuilderImpl2)); } /** * 打印输出套餐信息 */ public static void printMessage(MobilePackage mobilePackage){ System.out.println("--话费:"+mobilePackage.getMoney()+"\t短信:" +mobilePackage.getShortInfo()+"条\t彩铃:"+mobilePackage.getMusic()); } }
执行结果:
实现的代码结构:
从以上的代码不难看出,其实指导者只是一个中介服务而已,最终还是建造者设置实例对象,最后返回给终端客户,那为什么还要指导者角色呢?
指导者确实是一个中间的服务,但是其所起的作用是不能小觑的,正是指导者的角色起到了对构建者构建产品过程的封装,有效的减少了客户端使用构建者构建产品的各个过程。假如一个复杂产品的构建需要十几个过程甚至更多,如果在客户端一步一步使用构建者来构建复杂对象,那么客户端的复杂程度可想而知,最重要的一点是,客户端需要的是最终产品,构建过程客户端是不需要关心的。所以,指导者角色的存在是有一定道理的。
4.6使用场合
1)当生成的产品对象内部具有复杂的结构时;
2)当复杂对象需要与表示分离时,可能需要创建不同的表示时;
3)当需要向客户端隐藏产品内部结构的表现时。
建造者模式的优点在于其构建和表示的分离,有效地将复杂对象处理过程分解,降低功能模块之间的耦合度,增强模块内部的内聚度,使得其在软件设计模式中具有极其重要的位置。建造者设计模式的结构如图4-6所示:
其实,建造者模式可以简化处理,将指导者角色去掉。如图4-7所示:
经过以上的内容,你可以这样简单地理解建造者模式:在建造者内部存在一个具体产品的对象实例,所有的构建功能都是针对这个对象实例进行的, 而这个对象实例就是我们最终想要的东西或者是该对象实例的其他表现形式。这就相当于,先在水缸中存满足够的水,等到想喝水的时候不用打开水龙头接水喝,直接从缸里舀水就行。当然,即使水缸中没有接满足够的水(构建对象的各个功能是彼此独立的),你也还是能喝到水的(构建和表示分离)。
扩展:建造者模式和抽象工厂模式的区别
1)建造者模式着重于分步骤构造一个复杂对象,而抽象工厂模式则着重于多个系列的产品对象即对象族的构造;
2)建造者模式是在最后一步返回具体产品,而抽象工厂模式则是立即返回具体产品。