建造者模式(Builder)
建造者模式是用来解决产品对象的创建过程是由多个零件组成的情况,这些零件与产品本身是组合关系,也就是部分与整体,这些零件的创建顺序,还有一些创建中的逻辑,都是稳定的,可以封装起来的.
例如,一个邮件对象,要成功的使用前,需要设置主题,收件人,发件人,正文,附件等零件,这些零件都与邮件对象是组合关系,如果将这些创建逻辑散落在调用处,每次使用邮件对象时都要进行繁琐的装配零件工作,而且还有可能会有一定的业务逻辑掺在这其中.
建造者模式除了将构建产品零件的算法封装起来,还可以支持消费的产品对象的动态选择,当然这也是面向对象不变的话题,封装变化点,依赖倒置.具体产品是一个对象还是一系列,这个根据实际的业务设计有所不同.
在上图中展示了一个基础的建造者模式的结构,为了尽量说明实际情况的模式环境,我们这里描绘了一个系列的产品对象.红色字体部分代表的是实现层,也就是依赖倒置原则中所讲的,不依赖实现,而依赖抽象.这里的实现层在系统中肯定是频繁变化的,而且可以随时以扩展的方式改变功能(开闭原则),而蓝色表示的是系统中的抽象层,要求是稳定的,不太能够应对频繁的功能变化.
产品对象
先从产品类来讲起,这一块通常是核心的业务骨架,对于一组产品对象,现实系统中肯定比这里的结构还要复杂,读者视自身业务情况来设计,在上图中,Product代表的是抽象产品接口,这个接口描述依赖着ProductPart,ProductPart2两个抽象的零件.这之间的关系是组合关系,换言之,就是Product的生成是需要先创建ProductPart与ProductPart2两个零件.具体的实现零件则是ConcretePart与ConcretePart2两个类来完成. ConecreteProduct是产品的具体实现类.
可以看出,红色部分的几个Conecrete开头的类,都是可以应对系统变化的,也就是我们随时可以加入ConecretePratXX 或者 ConecreteProductXXX这样的新的实现来丰富我们的系统.
封装产品的创建过程
在介绍完上边的产品结构之后,我们应该了解到Product是依赖着ProductPart与ProductPart2这两个零件的,建造者模式要封装的是这个对象的创建过程,比如:先创建Part1再创建part2,或者说part2的必须是10个以上.这就是对创建算法的封装.
Builder是抽象的建造者,其中描述buildPart()与buildPart2()两个方法分别构建不同的零件.getProduct()返回最终的产品对象Product.
ConcreteBuilder是具体的构建实现,这里与ConcretePart,ConcretePart2两个具体零件对象是关联着的,如果加入新的一套不同风格的零件,则也相应的要增加XXXConcreteBuilder,当然,如果我们很确定系统只有一种风格,那大哥不必将Builder抽象化.
Director是一个导演类,用为将具体稳定的创建算法封装在这里,比如刚才所讲的,要求创建10个Part2对象.
最后我们来看Client类,这里是指代码调用处,依赖着产品的抽象层,完全不关心具体层,在调用时只需要选择不同的ConcreteBuilder就可以完成不同风格产品的组装过程.值得说明的一点,风格是指加入不同的实现类,比如再加一套新风格,就需要加入新的派生自Product , ProductPart , ProductPart2 ,Builder的几个实现类.
具体举例说明
写到这里,也不免俗套的要举一个其实和实际开发相距甚远,还有些套路化的例子了,虽然园子里关于设计模式的文章如汗牛充栋,也有很多口号讲,不要重复搞*.但小弟还是认为,搞*和写这些文章对个人的提高是很大的,同时也能在过程中得到与别人的互动交流,设计模式本来就还有一层意思,那就是交流.我们大家都约定俗成,交流起来就更方便了不是吗,好了,各们大哥,我可以开始了吗?
假如我们有一个游戏场景,需要一个房屋,这个房屋由多面墙壁,一块地板,一块天花板组成.我们很自然的可以写出房屋,墙壁,地板,天花板对象,而且房屋与其它对象是组合关系,表现在代码中应该就是成员变量.
由于系统要求房屋有着不同的风格,比如一款普通风格,另一款精致风格.这时我们很自然的应该将房屋,地板,天花板,墙壁都抽象化.这些对象就对应前面所讲的产品结构.
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 抽象房顶
*
* @author aladdinty
* @create 2018-01-10
**/
public abstract class AbsRoof
{
public abstract String getRoofInfo() ;
}
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 抽象墙壁
*
* @author aladdinty
* @create 2018-01-10
**/
public abstract class AbsWall
{
public abstract String getWallInfo() ;
}
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 抽象地板
*
* @author aladdinty
* @create 2018-01-10
**/
public abstract class AbsFloor
{
public abstract String getFloorInfo() ;
}
package com.j2kaka.coolka.examples.pattern.builder;
import java.util.ArrayList;
import java.util.List;
/**
* 抽象的房子类
* @author aladdinty
* @create 2018-01-10
**/
public abstract class AbsHouse
{
private List<AbsWall> wallList ;
private AbsFloor floor ;
private AbsRoof roof ;
/**添加墙壁*/
public void addWall (AbsWall wall)
{
if( this.wallList == null )
{
this.wallList = new ArrayList<AbsWall> () ;
}
this.wallList.add ( wall ) ;
}
/**设置地板*/
public void setFloor( AbsFloor floor )
{
this.floor = floor ;
}
/**设置天花板*/
public void setRoof( AbsRoof roof )
{
this.roof = roof ;
}
/**获取天花板*/
public AbsRoof getRoof ()
{
return roof;
}
/**获取地板*/
public AbsFloor getFloor ()
{
return floor;
}
/**获取所有墙壁*/
public List<AbsWall> getAllWall ()
{
return wallList;
}
public String getInfo()
{
StringBuffer sb = new StringBuffer () ;
for( AbsWall wall : this.wallList )
{
sb.append ( wall.getWallInfo () + "|") ;
}
return "房子的配置是这样的:" + "有几面墙:" + wallList.size () + "->" + sb.toString () + "->"+this.roof.getRoofInfo () +"->" + this.floor.getFloorInfo () ;
}
}
好抽象层的产品描述完成了,具体产品要有对应的实现了,这里分别提供普通款与精致款风格的不同实现.
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 普通地板
*
* @author aladdinty
* @create 2018-01-10
**/
public class CommonFloor extends AbsFloor
{
@Override
public String getFloorInfo ()
{
return "普通地板";
}
}
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 普通天花板
*
* @author aladdinty
* @create 2018-01-10
**/
public class CommonRoof extends AbsRoof
{
@Override
public String getRoofInfo ()
{
return "普通天花板";
}
}
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 普通风格的墙面
*
* @author aladdinty
* @create 2018-01-10
**/
public class CommonWall extends AbsWall
{
@Override
public String getWallInfo ()
{
return "普通墙面";
}
}
package com.j2kaka.coolka.examples.pattern.builder;
import java.util.ArrayList;
import java.util.List;
/**
* 普通房子风格实现
*
* @author aladdinty
* @create 2018-01-10
**/
public class CommonHouseImpl extends AbsHouse
{
}
再来一款精致款式实现
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 精致地板
*
* @author aladdinty
* @create 2018-01-10
**/
public class FinesseFloor extends AbsFloor
{
@Override
public String getFloorInfo ()
{
return "精致地板";
}
}
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 精致天花板
*
* @author aladdinty
* @create 2018-01-10
**/
public class FinesseRoof extends AbsRoof
{
@Override
public String getRoofInfo ()
{
return "精致天花板";
}
}
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 精致的的墙面
*
* @author aladdinty
* @create 2018-01-10
**/
public class FinesseWall extends AbsWall
{
@Override
public String getWallInfo ()
{
return "精致墙面";
}
}
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 精致风格实现
*
* @author aladdinty
* @create 2018-01-10
**/
public class FinesseHouseImpl extends AbsHouse
{
}
现目前为止,我们实现了产品的抽象化,不过还没体现出建造者模式的核心,我们开头就说过,要封装的是建造过程,同时可以动态选择产品的不同实现.让客户端毫不知情,下面代码中抽象的构建器描述我们的产品创建过程中用到的动作.最后要求返回房屋对象.
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 抽象建造者
*
* @author aladdinty
* @create 2018-01-10
**/
public abstract class AbsBuilder
{
public abstract void buildFloor() ;
public abstract void buildRoof() ;
public abstract void buildWall() ;
public abstract AbsHouse getHouse() ;
}
具体实现不同的构建器,因为我们这个举例中有2套风格,分别是普通与精致风格,产品结构也有了相应的抽象体现,所以Builder的实现也是一一对应的.在这两个具体实现类里边,都是直接关联了具体的对象产品.
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 普通风格的构建器实现
*
* @author aladdinty
* @create 2018-01-10
**/
public class CommonBuilderImpl extends AbsBuilder
{
private AbsHouse house = new CommonHouseImpl () ;
@Override
public void buildFloor ()
{
house.setFloor ( new CommonFloor ());
}
@Override
public void buildRoof ()
{
house.setRoof ( new CommonRoof ());
}
@Override
public void buildWall ()
{
house.addWall ( new CommonWall () );
}
@Override
public AbsHouse getHouse ()
{
return this.house;
}
}
package com.j2kaka.coolka.examples.pattern.builder;
/**
* 精致风格房屋构建器实现
*
* @author aladdinty
* @create 2018-01-10
**/
public class FinesseBuilderImpl extends AbsBuilder
{
private AbsHouse house = new FinesseHouseImpl () ;
@Override
public void buildFloor ()
{
house.setFloor ( new FinesseFloor ());
}
@Override
public void buildRoof ()
{
house.setRoof ( new FinesseRoof ());
}
@Override
public void buildWall ()
{
house.addWall ( new FinesseWall () );
}
@Override
public AbsHouse getHouse ()
{
return this.house;
}
}
好了,构建器完成了,产品对象也完成了,现在到我们的工头出场了,他一声令下,给我盖..这里的工具其实是对应之前结构中的Director类,将稳定的对象结构创建步骤和算法封装起来,我们这里简单要求,要有4面墙壁,还有一个地板和一个天花板.
```package com.j2kaka.coolka.examples.pattern.builder;
/**
- 工队领头人
- 这个类中代表的是稳定的构建算法,是不能适应频繁变化的.
- @author aladdinty
-
@create 2018-01-10
**/
public class Foreman
{
public static AbsHouse createMyHouse( AbsBuilder builder )
{//先盖地板 builder.buildFloor (); //接着修建四面墙壁 for (int i = 0; i < 4; i++) { builder.buildWall (); } //把房顶盖上.. builder.buildRoof (); return builder.getHouse () ;
}
}
最后我们看看调用处
package com.j2kaka.coolka.examples.pattern.builder;
/**
- 调用处代码
- @author aladdinty
-
@create 2018-01-10
**/
public class Client
{
public static void main(String[] args )
{
AbsHouse house = Foreman.createMyHouse ( new FinesseBuilderImpl () ) ;
System.out.println ( house.getInfo ());AbsHouse house2 = Foreman.createMyHouse ( new CommonBuilderImpl () ) ; System.out.println ( house2.getInfo ());
}
}
```
其实忙活了半天,只是为调用处以后可以应对变化时不做调整,变化全部封在了后面的结构中.在这里只需要知道产品的抽象接口,调用工头类,决定使用哪种风格的构建器就可以了.
好了,欢乐时光总是过的特别快,感谢您能看到这里,希望我的付出可以给您带来一点点的帮助,如果不爽请轻虐,如果疑问欢迎留言交流