建造者模式<Builder>
生活:
家家有本难念的经,人人有本难念的经,在城里,人们为了生存奔波着,每个人有自己的追求,或许为了未来能有个自己都模糊不清的好点的生活,或许是为了能在城里买一套房子,组建一个家庭,逃脱被城市边缘化的命运,于是有人选择了奋斗,有人选择了回忆,一个向前看,一个留在过去,向前看的人确定了自己的目标,根据自身的情况做出一个明确的职业规划,然后一步一个脚印,照着自己的路走下去。正在ta前进的时候,有退缩者感慨着:我要是有个他那样的明确的人生规划会怎么怎么样...是的,如果别人的路可以复制,那么每个人是独一无二的这句话就错了,成功千万条路,千变万化,没有固定的模式,追求自己的生活,走好自己的路。然而每一个人的一生变化是一致的,生老病死,谁也逃避不了,有人说每个人是公平的,因为生不带来,死不带去,每一个人 生---->死是固定的,所以可以用一个固定的模式概括人生:生到死的演变。
编程世界:
人都会经历生和死,这个是固定不变的,那么在程序世界中,可以用一个类来建造这一个固定的过程,因为不变,所以我们能将这个过程封装起来,用到的时候直接由一个类来负责建造。在比如JDBC中,往往会有几个固定不变的步骤:
1.加载驱动
2.创建连接
3.执行预编译
1.Class.forName(driver); 2.Connection conn = DriverManager.getConnection(url,userName,password); //设置预编译 String sql; 3.PreparedStatementpstm = conn.prepareStatement(sql);那么既然是不变的过程,是否可以考虑将这三个过程封装起来呢,换一个角度,这三个步骤分别由一个单独的类来负责,那么,此时有一个指挥者,他指挥这三个类一起工作,提供一个稳定的建造过程,得到我们需要的产品。
故事背景:
小时候的冬天充满着对雪的期盼,三五成群一起打起雪仗,雪越下越厚,有了造雪人的冲动...
面向对象分析:
雪人的造法,大家在清晰不过了,无非就是 身体--->左手---->右手---->头------>眼睛----->鼻子------>耳朵----->嘴巴,由于难度,退就省了,我们发现这个过程是固定不变的。当然有人说,我可以让它变,因为我没有做鼻子,那么这个就是一个错误了,我们要雪人是要一个成品,缺耳朵少鼻子的雪人是一个残品,不符合我们的需求。实际上,在每一个人建造的时候,难免会忽略一些东西,因为每一个人的技术和考虑不一样,同样一个人今天要是亢奋了,最后把嘴巴漏了,或者心情不好,干脆左手不要了,这样的雪人都是不合格的。造成了这个错误是违背了一个原则,那就是
依赖倒转原则--抽象不应该依赖于细节,细节应该依赖于抽象。这里见造雪人如果依赖于每一个人,就相当于依赖了每一个具体的人,耦合性高,难免会缺胳膊少退的(这里默认没有退)。
解决方案:将这个固定不变的过程抽取出来并进行封装,然后要创建这个雪人,就必须按照这几个步骤进行,这样就避免了残缺现象。
那么,建造者模式登场了:
将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。
a.对象的抽取:
抽象雪人建造者,SnowmanBuilder
具体的雪人建造者,高和矮个子:HighSnowmanBuilder,LowSnowmanBuilder,都继承了抽象的雪人建造者,有一个稳定不变的建造过程。
抽象指挥者:Director,调用雪人建造者的创建过程,依赖了抽象的雪人建造者。
具体指挥者袁慎建:Ysjian,继承自抽象指挥者。
产品雪人:Snowman,独立封装起来。
b.类的职责:SnowmanBuilder:
创建的过程:buildBody();buildLeftArm();buildRightArm();buildHead();
得到产品:getSnowman();
Director:
指挥创建雪人,createSnowman();
Snowman:
添加产品的结构组成,add(String part);
展示产品:show();
c.类的交互:Direct指挥者,依赖了抽象的雪人建造者类,由他来负责调用建造者的建造过程,客户端只需要与指挥者沟通,告诉他我要一个雪人就可以了。
UML类图:
代码实现:
产品Snowman:
public class Snowman { List<String> parts = new ArrayList<String>(); public void add(String part){ parts.add(part); } public void show(){ for (String part : parts) { System.out.println(part); } } }@这只是一个产品,进行了封装,便于测试。
抽象雪人建造者SnowmanBuilder:
/** * 一个建造雪人的抽象类 * * @author PingCX * */ public abstract class SnowmanBuilder { protected Snowman snowman = new Snowman(); /** * 建造身体 */ public abstract void buildBody(); /** * 建造左胳膊 */ public abstract void buildLeftArm(); /** * 建造右胳膊 */ public abstract void buildRightArm(); /** * 建造头 */ public abstract void buildHead(); /** * 获取创建的对象 * * @return */ public abstract Snowman getSnowman(); }@提供了一个稳定的抽象建造行为,让具体的建造者去实现这些行为,每一个行为就是一个建造的表示。
HighSnowmanBuiler:
/** * 高个子雪人建造者类 * @author PingCX * */ public class HighSnowmanBuilder extends SnowmanBuilder { @Override public void buildBody() { snowman.add("高个子雪人身体,身高195cm..."); } @Override public void buildLeftArm() { snowman.add("高个子雪人左胳膊,长70cm..."); } @Override public void buildRightArm() { snowman.add("高个子雪人右胳膊,长70cm..."); } @Override public void buildHead() { snowman.add("高个子雪人头..."); } @Override public Snowman getSnowman() { return snowman; } }LowSnowmanBuilder:
/** * 矮个子雪人建造者类 * @author PingCX * */ public class LowSnowmanBuilder extends SnowmanBuilder { @Override public void buildBody() { snowman.add("矮个子雪人身体,身高155cm..."); } @Override public void buildLeftArm() { snowman.add("矮个子雪人左胳膊,长55cm..."); } @Override public void buildRightArm() { snowman.add("矮个子雪人右胳膊,长55cm..."); } @Override public void buildHead() { snowman.add("矮个子雪人头..."); } @Override public Snowman getSnowman() { return snowman; } }
重要角色登场:
Director:
/** * 抽象指挥者类 * @author PingCX * */ public abstract class Director { protected SnowmanBuilder sBuilder; /** * 指挥创建雪人的过程 */ public abstract void createSnowman(); }@这个指挥者中有一个抽象建造,提供了一个创建雪人的接口,可以理解为,这个指挥者指挥这个建造者进行建造, 指挥者将创建的过程与它的表示分离了,使同样的创建过程可以创建不同表示的产品。即便建造者开小差,还有我指挥者,当然指挥者不能开小差的,为什么不能,就看具体指挥者怎么实现抽象指挥者的这个创建的行为的:
具体指挥者Ysjian:
/** * 具体指挥者类,我自己 * @author PingCX * */ public class Ysjian extends Director { public Ysjian(SnowmanBuilder sBuilder){ this.sBuilder = sBuilder; } @Override public void createSnowman() { sBuilder.buildBody(); sBuilder.buildLeftArm(); sBuilder.buildRightArm(); sBuilder.buildHead(); } }@在createSnowman这个具体实现中,逐一调用了建造者的那几个建造过程,这里就依赖了抽象的建造者。至于为什么指挥者不能开小差,我想很明显了,因为要将整个建造过程都完成,加入在实现中忘记了调用某一个方法,得到的就是残品了,所以领导要比下属多付点责任的,不然你这个领导的薪水也不是白拿的。
此时客户来了,有生意做了:
Client:
Client:
public class Client { public static void main(String[] args) { HighSnowmanBuilder hBuilder = new HighSnowmanBuilder(); Director ysjian = new Ysjian(hBuilder); ysjian.createSnowman(); Snowman snowman = hBuilder.getSnowman(); snowman.show(); System.out.println("----------------------------------"); LowSnowmanBuilder lBuilder = new LowSnowmanBuilder(); Director ysjian2 = new Ysjian(lBuilder); ysjian2.createSnowman(); Snowman snowman2 = lBuilder.getSnowman(); snowman2.show(); } }
@客户告诉指挥者,我要一个高个子雪人,这是指挥者就告诉高个子雪人建造工匠,你赶紧给我造一个,然后就一步步的创建,当然这个创建细节客户不用知道,用户只需要他的产品,所以很好的降低了耦合性。
小结:留心的人会注意到,上面一直说提供一个稳定的建造过程,而且要建造的对象内部实现是不同的, 所以建造者模式用于那种建造过程稳定的,但对象内部通常面临着复杂的变化的对象。由于产品的建造过程是稳定的,我们如果需要一个胖雪人,只需要写一个新的类继承自抽象建造者类,然后在具体实现中写相应的代码就可以了,扩展性很强。
小结:留心的人会注意到,上面一直说提供一个稳定的建造过程,而且要建造的对象内部实现是不同的, 所以建造者模式用于那种建造过程稳定的,但对象内部通常面临着复杂的变化的对象。由于产品的建造过程是稳定的,我们如果需要一个胖雪人,只需要写一个新的类继承自抽象建造者类,然后在具体实现中写相应的代码就可以了,扩展性很强。
当一个对象的算法应该独立于这个对象的组成部分以及它们的装配方式时,可以考虑使用建造者模式。
尾声:向前看,不要沉溺于过去,用自己的努力去建造一个属于自己的人生,也许我们逃不了生老病死,坦然面对,至少当自己在人生的尽头时没有太多的遗憾,付出了,也收获了...