一引子
前几天陪朋友去装机店攒了一台电脑看着装机工在那里熟练的装配着机器不禁想起来了培训时讲到的建造模式作为装机工他们不用管你用的CPU是Intel还是AMD也不管你的显卡是千大元还是白送的都能三下五除二的装配在一起——一台PC就诞生了!当然对于客户来说你也不知道太多关于PC组装的细节这和建造模式是多么的相像啊!
今天就来探讨一下建造模式
二定义与结构
GOF给建造模式的定义为将一个复杂对象的构建与它的表示分离使得同样的构建过程可以创建不同的表示可以将建造模式的精髓概括为将构造复杂对象的过程和对象的部件解耦这是对降低耦合提高可复用性精神的一种贯彻其实这种精神贯彻在GOF几乎所有的设计模式中
是不是和上面提到的装机流程相像?
这个很多人认为同抽象工厂模式相似的建造模式用在什么样的设计环境下呢(对于两者的比较稍候讨论)?我认为可以总结为以下环境当要生成的产品有复杂的内部结构其中的内部结构由多个对象组成系统将来可能要改变产品对象的内部结构的构成或者实现方式比如说产品的一些属性现在是从数据库中得到的而将来可能从XML中解析得到而且不能将产品的内部构造完全暴露给客户程序一是为了可用性二是为了安全等因素满足上面的设计环境就可以考虑使用建造模式来搭建框架了 来看看建造模式的组成吧
抽象建造者角色这个角色用来规范产品对象的各个组成成分的建造一般而言此角色独立于应用程序的商业逻辑
具体建造者角色担任这个角色的是于应用程序紧密相关的类它们在指导者的调用下创建产品实例这个角色在实现抽象建造者角色提供的方法的前提下达到完成产品组装提供成品的功能
指导者角色调用具体建造者角色以创建产品对象指导者并没有产品类的具体知识真正拥有产品类的具体知识的是具体建造者对象 产品角色建造中的复杂对象它要包含那些定义组件的类包括将这些组件装配成产品的接口
来看下这些角色组成的类图
首先客户程序创建一个指导者对象一个建造者角色并将建造者角色传入指导者对象进行配置然后指导者按照步骤调用建造者的方法创建产品最后客户程序从建造者或者指导者那里得到产品
从建造模式的工作流程来看建造模式将产品的组装外部化到了建造者角色中来这是和任何正规的工厂模式不一样的——产品的创建是在产品类中完成的
三实现
实在找不到太好的例子我认为《java与模式》中发邮件的例子还算可以这里我将《Think in Patterns with Java》中的例子放到这里权且充个门面媒体可以存在不同的表达形式比如书籍杂志和网络这个例子表示不同形式的媒体构造的步骤是相似的所以可以被提取到指导者角色中去
import javautil*;
import junitframework*;
//不同的媒体形式:
class Media extends ArrayList {}
class Book extends Media {}
class Magazine extends Media {}
class WebSite extends Media {}
// 进而不含不同的媒体组成元素:
class MediaItem {
private String s;
public MediaItem(String s) { thiss = s; }
public String toString() { return s; }
}
class Chapter extends MediaItem {
public Chapter(String s) { super(s); }
}
class Article extends MediaItem {
public Article(String s) { super(s); }
}
class WebItem extends MediaItem {
public WebItem(String s) { super(s); }
}
// 抽象建造者角色它规范了所有媒体建造的步骤:
class MediaBuilder {
public void buildBase() {}
public void addMediaItem(MediaItem item) {}
public Media getFinishedMedia() { return null; }
}
//具体建造者角色
class BookBuilder extends MediaBuilder {
private Book b;
public void buildBase() {
Systemoutprintln(Building book framework);
b = new Book();
}
public void addMediaItem(MediaItem chapter) {
Systemoutprintln(Adding chapter + chapter);
badd(chapter);
}
public Media getFinishedMedia() { return b; }
}
class MagazineBuilder extends MediaBuilder {
private Magazine m;
public void buildBase() {
Systemoutprintln(Building magazine framework);
m = new Magazine();
}
public void addMediaItem(MediaItem article) {
Systemoutprintln(Adding article + article);
madd(article);
}
public Media getFinishedMedia() { return m; }
}
class WebSiteBuilder extends MediaBuilder {
private WebSite w;
public void buildBase() {
Systemoutprintln(Building web site framework);
w = new WebSite();
}
public void addMediaItem(MediaItem webItem) {
Systemoutprintln(Adding web item + webItem);
wadd(webItem);
}
public Media getFinishedMedia() { return w; }
}
//指导者角色也叫上下文
class MediaDirector {
private MediaBuilder mb;
public MediaDirector(MediaBuilder mb) {
thismb = mb; //具有策略模式相似特征的
}
public Media produceMedia(List input) {
mbbuildBase();
for(Iterator it = erator(); ithasNext();)
mbaddMediaItem((MediaItem)itnext());
return mbgetFinishedMedia();
}
};
//测试程序——客户程序角色
public class BuildMedia extends TestCase {
private List input = ArraysasList(new MediaItem[] {
new MediaItem(item) new MediaItem(item)
new MediaItem(item) new MediaItem(item)
});
public void testBook() {
MediaDirector buildBook = new MediaDirector(new BookBuilder());
Media book = buildBookproduceMedia(input);
String result = book: + book;
Systemoutprintln(result);
assertEquals(result book: [item item item item]);
}
public void testMagazine() {
MediaDirector buildMagazine = new MediaDirector(new MagazineBuilder());
Media magazine = buildMagazineproduceMedia(input);
String result = magazine: + magazine;
Systemoutprintln(result);
assertEquals(result magazine: [item item item item]);
}
public void testWebSite(){
MediaDirector buildWebSite = new MediaDirector(new WebSiteBuilder());
Media webSite = buildWebSiteproduceMedia(input);
String result = web site: + webSite;
Systemoutprintln(result);
assertEquals(result web site: [item item item item]);
}
public static void main(String[] args) {
junittextuiTestRunnerrun(BuildMediaclass);
}
}
在实现的时候抽象建造角色提供的接口必须足够普遍以适应不同的具体建造角色对于一个建造角色来说可能某个步骤是不需要的可以将此接口实现为空多个产品之间可能没有太多的共同点可以提供一个标示接口作为抽象产品角色也可以不提供抽象产品角色这时要将提供产品的接口从抽象建造角色里面去掉不然就会编译出问题
四应用优点
建造模式可以使得产品内部的表象独立变化在原来的工厂方法模式中产品内部的表象是由产品自身来决定的而在建造模式中则是外部化为由建造者来负责这样定义一个新的具体建造者角色就可以改变产品的内部表象符合开闭原则
建造模式使得客户不需要知道太多产品内部的细节它将复杂对象的组建和表示方式封装在一个具体的建造角色中而且由指导者来协调建造者角色来得到具体的产品实例
每一个具体建造者角色是毫无关系的
建造模式可以对复杂产品的创建进行更加精细的控制产品的组成是由指导者角色调用具体建造者角色来逐步完成的所以比起其它创建型模式能更好的反映产品的构造过程
五扩展
建造模式中很可能要用到组成成品的各种组件类对于这些类的创建可以考虑使用工厂方法或者原型模式来实现在必要的时候也可以加上单例模式来控制类实例的产生但是要坚持一个大前提就是要使引入的模式给你的系统带来好处而不是臃肿的结构 建造模式在得到复杂产品的时候可能要引用多个不同的组件在这一点上来看建造模式和抽象工厂模式是相似