the5fire的技术博客

关注python、vim、linux、web开发和互联网--life is short, we need python.


重温设计模式之建造者模式(Builder)

作者:the5fire | 标签:     | 发布:2010-12-24 11:23 a.m. | 阅读量: 5335, 5315
在说Builder模式之前先来看看先前的Template Method模式:

TemplateMethod

试想一下如果把这个里面的TemplateMethod移动到另一个类中,也就是用另外一个类来控制算法会是怎么样的一个结果呢,来看这张图:



这样的话有什么好处呢?其实好处很多,最总要的就是这是一种“打破”,打破了原先算法骨架的死板,这里的TemplateMethod可以更加灵活,可以通过子类来实现对不同ConcreteClass的依赖,从而可以达到在客户端通过不同的AnotherClass的子类(用多态和工厂很容易实现)灵活的完成操作,而不需要知晓藏在AnotherClass后面的AbstractClass。(我怎么觉得这里有些类似于工厂方法呢。。。这个以后再谈)

再继续推演即可得到本文要说的Builder模式:
builder

对比一下这个图和上面的那个图,你会发现,这俩简直就是双胞胎嘛。唯一不同的是这里最后产生的是一个Product,而模板方法中并不会返回一个Product类,或者说只是执行某一算法,没有返回任何东西。

所以这时就不难理解builder模式的精髓所在——“定义算法骨架”,用GoF的话讲叫:“同样的构建过程可以创建不同的表示”。不过这里比模板方法更胜一筹的在于“将复杂对象的构建与它的表示分离

那么还是需要再来看看Builder模式的定义以及基本实现代码:

官方定义:建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

代码:

package builder;

import java.util.*;

/**
* 产品类
* @author the5fire
*
*/
public class Product {
List parts = new ArrayList();

public void Add(String part) {
parts.add(part);
}

public void Show() {
System.out.println("\n产品 创建 -----");
for (String part : parts) {
System.out.println(part);
}
}
}
package builder;
/**
* 建造者抽象类
* @author the5fire
*
*/
abstract class Builder {
public abstract void buildPartA();
public abstract void buildPartB();
public abstract Product getResult();
}
package builder;

/**
* 具体建造者1
* @author the5fire
*
*/
public class ConcreteBuilder1 extends Builder {
private Product product = new Product();

@Override
public void buildPartA() {
product.Add("部件A");

}

@Override
public void buildPartB() {
product.Add("部件B");

}

@Override
public Product getResult() {

return product;
}

}
package builder;

/**
* 具体建造者2
* @author the5fire
*
*/
public class ConcreteBuilder2 extends Builder {
private Product product = new Product();

@Override
public void buildPartA() {
product.Add("部件X");

}

@Override
public void buildPartB() {
product.Add("部件Y");

}

@Override
public Product getResult() {

return product;
}

}
package builder;

/**
* 指挥者,指挥产品的构建
* @author the5fire
*
*/
public class Director {
public void Construct(Builder builder) {
builder.buildPartA();
builder.buildPartB();
}
}
package builder;

public class Client {

/**
* @param args
*/
public static void main(String[] args) {
Director director = new Director();

Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2();

director.Construct(b1);
Product p1 = b1.getResult();
p1.Show();

director.Construct(b2);
Product p2 = b2.getResult();

p2.Show();

}

}



通过代码可以直观的看到,Builder中关键的部分就是Director这个类,由他主导了整个Product的构建。不过这个Product类也不是必须的,因为产品不一定非要得到最后返回到客户端,也可以是添加某一个本地文件,也可能是向数据库中添加一系列的数据。总之,设计模式,不要那么死板的用。

再来看看Builder模式的应用:

从定义不难得出:凡是需要按照某一固定结构添加部件,然后又返回产品的就可以看成是Builder方法。

这几天一直在写一个和数据库打交道的项目,里面用到的比较多的就是java.sql.PreparedStatement这个类,它的使用步骤就是先添加参数,什么String型的,Integer型的,添加完成之后通过execute来执行,返回结果。可以看得出来,这个显然是去掉Director之后的用法。(你看出来了吗?试着自己写写)

当然在JDK源码中还有其他地方用到了这个模式,比如说java.lang.StringBuilder和java.lang.StringBuffer中的append方法,这个也是比较常用的方法,看看两个类的源码,你会发现其实这两个类关于Builder模式的使用可以说是进行了扩展,其中并不像上面写的那些比较“单纯”的代码,里面进行了一些很实际很必要的处理,也就是在添加部件的时候进行处理。大家可以自己看一下源码(路人甲:俺不会看;我:google去)

关于Builder模式的扩展:

建造者模式的初衷就是将复杂对象的构建和其表示分离,这样的好处就是在客户端调用的时候不需要了解对象内部,我只需要知道我要创建什么样的Product就行了。

然而,你看上面我写的那个客户端,它需要知道Director类,还需要知道Builder类、ConcreteBuilder类,和Product类,虽说这不过只是知道其类就行了,但依然不能到达最佳的“最少知道”原则。

如果把ConcreteBuilder的创建放到Director内部,关于产品的返回也需要放到Director内部,具体是什么产品也要放到Director内部。那么在客户端怎么创建出来产品呢?创建什么产品呢?

其实按照实际应用,客户端看作是Web页面的话,系统能够创建什么样的产品,应该会显示一个列表的,客户端就可以把这个列表中的产品传到Director中去,这时传递的就是字符串,在Director中就可根据字符串内容来创建不同的Builder类,不同的Product类,然后构建,然后返回。这里可以使用反射来达到最佳灵活度。

这里给出示例类图:


----EOF-----

扫码关注,或者搜索微信公众号:码农悟凡


其他分类: