Java设计模式之建造者模式

概论

 什么是建造者模式呢?将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

 

建造者模式示例

我们先来回忆一下在模板模式中的 示例,[内外部系统交互]中的四个步骤:

第一步:参数校验

第二步:封装对外的请求参数

第三步:对外提交请求

第四步:后置处理,例如记录操作日志

 

最后核心的算法设计在run方法中。如下代码所示:

 1 public void run (ExampleContext context) {
 2 
 3         validate(context);
 4 
 5         prepare(context);
 6 
 7         proccess(context);
 8 
 9         after(context);
10     }

客户端只要取调用run方法就可以。一切看来都很美好,但是如果我们想要把第一步和第二步交换执行顺序。或者把第二步舍弃。或者第三步和第四步交换执行顺序等等。

 

简单的描述一下问题:我们想要在一个算法中若干个步骤,客户端需要自定义步骤的顺序以及确定是否采用或者舍弃一些步骤。

此时需要用到建造者模式。首先我们需要有抽象产品类:

 1 public abstract class AbstractProccessor {
 2 
 3     private List<String> sequence = new ArrayList<String>();
 4 
 5 
 6 
 7     public boolean validate(ExampleContext context) {
 8         if (context == null) {
 9             return false;
10         }
11 
12         return true;
13     }
14 
15     public abstract void prepare(ExampleContext context);
16 
17     public abstract void proccess(ExampleContext context);
18 
19     public abstract void after(ExampleContext context);
20 
21     protected boolean needAfterProccessing () {
22         return true;
23     }
24 
25 
26     public List<String> getSequence() {
27         return sequence;
28     }
29 
30     public void setSequence(List<String> sequence) {
31         this.sequence = sequence;
32     }
33 
34     public void run (ExampleContext context) {
35 
36         for(String methodName : sequence) {
37             if(StringUtils.isEmpty(methodName)) {
38                 continue;
39             }
40 
41             Method method = null;
42 
43             try {
44                  method = this.getClass().getMethod(methodName, ExampleContext.class);
45             } catch (Exception e) {
46 
47             }
48 
49             if(method == null) {
50                 continue;
51             }
52 
53             try {
54                 method.invoke(this, context);
55             } catch (Exception e) {
56 
57             }
58 
59 
60         }
61     }
62 
63 }

第3行:声明一个表示顺序的集合,集合中存储的是算法中每个步骤的方法名。类型为String类型。

第31行-61行:run方法通过遍历顺序集合的方式依次执行。通过反射的方式来调用。

 

接下来,和模板方法中的两个具体产品类完全一样,一个是HttpProccessor,另外一个是OtherProccessor。代码如下所示:

 1 public class HttpProccessor extends AbstractProccessor {
 2 
 3     protected boolean needAfterProccessing = false;
 4 
 5     @Override
 6     public void prepare(ExampleContext context) {
 7 
 8         System.out.println("http 前置处理");
 9 
10     }
11 
12     @Override
13     public void proccess(ExampleContext context) {
14 
15         System.out.println("http 提交请求");
16 
17     }
18 
19     @Override
20     public void after(ExampleContext context) {
21 
22         System.out.println("http 后置处理");
23 
24     }
25     
26 }
 1 public class OtherProccessor extends AbstractProccessor {
 2 
 3     @Override
 4     public void prepare(ExampleContext context) {
 5 
 6         System.out.println("other 前置处理");
 7 
 8     }
 9 
10     @Override
11     public void proccess(ExampleContext context) {
12 
13         System.out.println("other 提交请求");
14 
15     }
16 
17     @Override
18     public void after(ExampleContext context) {
19 
20         System.out.println("other 后置处理");
21 
22     }
23 }

最后我们需要修改一下我们的场景类Client:

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4 
 5         AbstractProccessor proccessor = new HttpProccessor();
 6 
 7         List<String> list = new ArrayList<String>();
 8         list.add("prepare");
 9         list.add("proccess");
10         proccessor.setSequence(list);
11 
12         proccessor.run(new ExampleContext());
13 
14 
15     }
16 }

第7行-10行:设置要执行的步骤以及顺序。

运行结果如下所示:

http 前置处理
http 提交请求

 

 当我们需要很多种场景,是不是需要去写很多种负责的场景类?在建造者设计模式中引入一个很重要的角色--建造者。场景类需要什么顺序,只需要告诉建造者就行。

我们新增一个建造者抽象类AbtractorBuilder。

1 public abstract class AbstractBuilder {
2 
3     public abstract void setSequence(List<String> list);
4 
5     public abstract AbstractProccessor  getProccessor();
6 }

 

再新增一个HttpBuilder和OtherBuilder

public class HttpBuilder extends AbstractBuilder {

    private AbstractProccessor proccessor = new HttpProccessor();

    @Override
    public void setSequence(List<String> list) {
        proccessor.setSequence(list);
    }

    @Override
    public AbstractProccessor getProccessor() {
        return proccessor;
    }


}

 

public class OtherBuilder extends AbstractBuilder {

    private AbstractProccessor proccessor = new OtherProccessor();

    @Override
    public void setSequence(List<String> list) {
          proccessor.setSequence(list);
    }

    @Override
    public AbstractProccessor getProccessor() {
        return proccessor;
    }


}

 

继续修改一下场景类:

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4 
 5         List<String> list = new ArrayList<String>();
 6         list.add("prepare");
 7         list.add("proccess");
 8 
 9         HttpBuilder httpBuilder = new HttpBuilder();
10         httpBuilder.setSequence(list);
11 
12         AbstractProccessor proccessor = httpBuilder.getProccessor();
13         proccessor.run(new ExampleContext());
14 
15     }
16 }

 

但是我们的HttpBuilder还是在场景类中调用时,顺序还是不够灵活。 这时需要产生一个导演类,将所有预知的情况 组合的情况一一列出来。如下代码所示:

 1 public class Director {
 2 
 3     private List<String> sequence = new ArrayList<String>();
 4     private AbstractBuilder httpBuilder = new HttpBuilder();
 5     private AbstractBuilder otherBuilder = new OtherBuilder();
 6 
 7     public AbstractProccessor getHttpProccessorFirst() {
 8 
 9         this.sequence.clear();
10         sequence.add("prepare");
11 
12         httpBuilder.setSequence(sequence);
13 
14         AbstractProccessor proccessor = httpBuilder.getProccessor();
15 
16         return proccessor;
17     }
18 
19     public AbstractProccessor getHttpProccessorSecond() {
20 
21         this.sequence.clear();
22         sequence.add("prepare");
23         sequence.add("after");
24 
25         httpBuilder.setSequence(sequence);
26 
27         AbstractProccessor proccessor = httpBuilder.getProccessor();
28 
29         return proccessor;
30     }
31 
32     public AbstractProccessor getOtherProccessorFirst() {
33 
34         this.sequence.clear();
35         sequence.add("prepare");
36 
37         otherBuilder.setSequence(sequence);
38 
39         AbstractProccessor proccessor = otherBuilder.getProccessor();
40 
41         return proccessor;
42     }
43 
44     public AbstractProccessor getOtherProccessorSecond() {
45 
46         this.sequence.clear();
47         sequence.add("prepare");
48         sequence.add("after");
49 
50         otherBuilder.setSequence(sequence);
51 
52         AbstractProccessor proccessor = otherBuilder.getProccessor();
53 
54         return proccessor;
55     }
56 }

 

最后我们在修改一下场景类:

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4 
 5         AbstractProccessor proccessor = null;
 6 
 7         Director director = new Director();
 8         proccessor = director.getHttpProccessorFirst();
 9         proccessor.run(new ExampleContext());
10 
11         proccessor = director.getHttpProccessorSecond();
12         proccessor.run(new ExampleContext());
13 
14 
15         proccessor = director.getOtherProccessorFirst();
16         proccessor.run(new ExampleContext());
17 
18         proccessor = director.getOtherProccessorSecond();
19         proccessor.run(new ExampleContext());
20 
21 
22 
23     }
24 }

简单而明了,笔者在开发的过程中,遇到很多的开发者不注意代码质量,甚至连资深的技术专家都没有意识到这一点。写下一行代码需要10秒钟,而看懂一行代码确实需要很大的力气。因此设计模式是用简单的方式将复杂的业务需求清晰的表达出来。

 

最后总结一下。建造者模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

 

在建造者模式中有4个角色,分别是产品类,抽象建造者,具体建造者,导演。

 

建造者模式的优点

1.良好的封装:场景类不必知道产品内部的细节。

2.建造者的独立,良好的扩展:存在不同的具体建造者,每个建造者之间独立。

3.良好的控制风险:因为具体的建造者是相互独立的,因此建造过程也是独立的,不会某一个建造者的变动对其他造成影响。

 

posted @ 2018-11-28 19:24  冰糖小城  阅读(293)  评论(0编辑  收藏  举报