文档章节

JAVA设计模式之模板方法模式和建造者模式

木木匠
 木木匠
发布于 11/17 22:28
字数 2371
阅读 16
收藏 7

一、前期回顾

上一篇《Java 设计模式之工厂方法模式与抽象工厂模式》介绍了三种工厂模式,分别是工厂方法模式,简单工厂方法模式,抽象工厂模式,文中详细根据实际场景介绍了三种模式的定义,实践,最后总结了三种方式的区别,以及各个模式的适用场景。这一篇博文我们来学习下模板方法模式和建造者模式。

二、模板方法模式和建造者模式的定义与实践

2.1 模板方法模式的定义和实践

定义:Define the skeleton of an algorithm in an operation.deferring some steps to subclasses.Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

翻译:定义一个操作中的算法框架,从而延迟子类中的一些步骤。使得子类可以不改变算法结构的情况下就可以重新定义该算法的某些特定的步骤。

其实根据上面定义,我们很快就可以想到,这个模式的定义不就是继承吗?对,没错,模板方法模式最简单的实现就是继承。我们先用代码来实现一个模板方法模式。相信大家都吃过泡面吧,我们来用模板方法模式来泡个面吧。

public abstract class AbstractTemplate {
    /**
     * 烧开水*/
    public abstract void boilWater();
    /**煮面条*/
    public abstract void cookNoodles();
    /**放调料*/
    public abstract void putCondiment();
    /**定义煮面的模板,先烧水,再放面条,最后放调料*/
    public void finish(){
        boilWater();
        cookNoodles();
        putCondiment();
        System.out.println("煮完啦,开吃咯!");
    }
}
/**煮方便面模板实现类*/
public class InstantNoodlesTemplate extends AbstractTemplate{
    @Override
    public void boilWater() {
        System.out.println("烧开水啦!");
    }

    @Override
    public void cookNoodles() {
        System.out.println("放入方便面啦!");
    }

    @Override
    public void putCondiment() {
        System.out.println("可以放调料啦!");
    }
}
/**客户端场景类*/
public class Client {
    public static void main(String[] args) {
        AbstractTemplate template=new InstantNoodlesTemplate();
        template.finish();
    }
}

上面的的finish()方法就是定义了一个算法框架,定义了煮面条先要烧开水,然后放面条,最后放调料的算法步骤。然后各个子类实现如何烧水,放什么面条,放什么调料这样的具体实现。这就是模板方法模式,仅仅通过继承就实现了。就是这么简单,下面我们来看看和模板方法模式相识的另外一个模式,建造者模式。

2.21 建造者模式的定义和实践

定义:Separate the construction of a complex object from its representation so that the same construction process can create different representations.

翻译:将一个复杂对象的构建与他的表现分离,使得同样的构建过程可以创建不同的表示。

这里化重点,复杂对象的构建和表现分离,这里用在上面煮面的场景中就是说,煮面的算法和定义是分离的,也就是说由各个具体的面条品种决定如何去煮面条,如何去放调料等等这些具体的算法步骤。我们来用代码实现下:

//抽象煮苗条类
public abstract class AbstractNoodlesMode {
    private List<String> stepOrders = new ArrayList<>();

    /**
     * 烧开水
     */
    public abstract void boilWater();

    /**
     * 煮面条
     */
    public abstract void cookNoodles();

    /**
     * 放调料
     */
    public abstract void putCondiment();

    /**
     * 煮面条其他步骤
     */
    public abstract void doOther();
    /**
     * 根据传入的工序进行加工煮面
     */
    final public void finish() {
        for (String stepOrder : this.stepOrders) {
            switch (stepOrder) {
                case "boilWater":
                    boilWater();
                    break;
                case "cookNoodles":
                    cookNoodles();
                    break;
                case "putCondiment":
                    putCondiment();
                    break;
                case "doOther":
                    doOther();
                    break;
                default:
                    System.out.println("无法识别的烹饪指令");
                    break;
            }
        }
    }

    final public void setStepOrders(List<String> stepOrders) {
        this.stepOrders = stepOrders;
    }
}
/**方便面的实现类,由于只需要烧水,煮面,放调料就行了,other方法就为空*/
public class InstantNoodles extends AbstractNoodlesMode {
    @Override
    public void boilWater() {
        System.out.println("煮开水");
    }

    @Override
    public void cookNoodles() {
        System.out.println("放入方便面");
    }

    @Override
    public void putCondiment() {
        System.out.println("放入调料");
    }

    @Override
    public void doOther() {

    }
}
/**意大利面条实现类*/
public class Spaghetti extends AbstractNoodlesMode {
    @Override
    public void boilWater() {
        System.out.println("煮开水");
    }

    @Override
    public void cookNoodles() {
        System.out.println("放入意大利面");
    }

    @Override
    public void putCondiment() {
        System.out.println("放入番茄酱");
    }

    @Override
    public void doOther() {
        System.out.println("放入火腿,早餐肉");
    }
}
/**抽象建造者类*/
public abstract class AbstractBuilder {
    /**定义煮面条工序*/
    public abstract AbstractBuilder cookNoodles();
    /**完成面条*/
    public abstract AbstractNoodlesMode build();
}
/**泡面建造者实现类*/
public class InstantNoodlesBuilder extends AbstractBuilder {
    private AbstractNoodlesMode noodles=new InstantNoodles();
    @Override
    public AbstractBuilder cookNoodles() {
        List<String> steps=new ArrayList<>();
        //烧水
        steps.add("boilWater");
        //放面条
        steps.add("cookNoodles");
        //放调料
        steps.add("putCondiment");
        this.noodles.setStepOrders(steps);
        return this;
    }

    @Override
    public AbstractNoodlesMode build() {
        return this.noodles;
    }

}

/*意大利面条建造者实现类**/
public class SpaghettiBuilder extends AbstractBuilder {
    private AbstractNoodlesMode noodle = new Spaghetti();

    @Override
    public AbstractBuilder cookNoodles() {
        List<String> steps = new ArrayList<>();
        //烧水
        steps.add("boilWater");
        //放面条
        steps.add("cookNoodles");
        //放调料
        steps.add("putCondiment");
        //放火腿,放早餐肉
        steps.add("doOther");
        this.noodle.setStepOrders(steps);
        return this;
    }

    @Override
    public AbstractNoodlesMode build() {
        return this.noodle;
    }
}

/**客户端场景类*/
public class Client {
    public static void main(String[] args) {
        AbstractBuilder instantNoodleBuilder = new InstantNoodlesBuilder();
        AbstractBuilder spaghettiBuilder = new SpaghettiBuilder();
        AbstractNoodlesMode instantNoodle = instantNoodleBuilder.cookNoodles().build();
        instantNoodle.finish();
        System.out.println("--------------------");
        AbstractNoodlesMode spaghe = spaghettiBuilder.cookNoodles().build();
        spaghe.finish();
    }
}

我们运行下结果如下:

上述代码我们整理成一个类图如下:

上面类图我省去了面条实现类,但是这也不影响整个建造者模式的理解。这里有同学肯定会问,这个建造者模式虽然比模板方法模式复杂点,但是感觉他们很相似啊,有点弄混的感觉啊。对,没错,他们确实很相识。但是我们只要记住一点就能很好的区分他们。

  • 模板方法模式已经定义好了建造者的算法,也就是工序,先做什么后做什么。
  • 建造者模式没有定义建造者的工序,而是交给子类去实现建造者的算法,也就是工序。

我们只要记住这一点就能很好的区分模板方法模式和建造者模式,同时也就知道了什么时候采用模板方法模式,什么时候采用建造者模式。

三、模板方法模式和建造者模式的应用场景

3.1 模板方法模式的使用场景

1.多个子类有公有的方法,并且逻辑基本一样时。

2.有重要核心的复杂算法,而且需要很多的复用性时,可以把该算法用模板方法模式实现。

3.代码重构升级时,模板方法模式是很常用的模式,一般为了保证向下兼容,所以采用模板方法模式,在模板方法内会设置钩子函数,根据不同的版本和不同的情况执行不同的算法。

3.2 建造者模式的使用场景

1.相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式

2.多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。

四、模板方法模式和建造者模式的优点与缺点

4.1模板方法模式

优点:

  • 良好的扩展性,子类只要实现指定的算法即可,不用关注后续组合的算法
  • 良好的可维护性。
  • 符合依赖倒置原则

缺点:

  • 个人理解的缺点可能就是封装死了核心组合方法,但是这就是模板方法的特点,姑且算一个缺点吧。

4.2建造者模式

优点:

  • 和模板方法模式一样具有良好的扩展性和可维护性
  • 灵活性,由于连最核心的组合算法都交给子类去实现,所以更灵活。

缺点:

  • 风险性,由于放开了组合算法,降低了约束性。所以可能会导致子类调用定义错了算法,给系统带来潜在的风险。

五、总结

模板方法模式和建造者模式都是属于创造性模式,两个模式代码很相似,很容易弄混,我们只要记住他们的核心区别就可以知道什么时候该用模板方法模式,什么时候该用建造者模式。这里再说一遍他们的主要区别: 模板方法模式定义了核心算法,也就是组装工序,而建造者模式没有定义建造的顺序和构造多少零件,最后的核心工序以及使用零件实现完全由子类去决定。

详细细心的读者肯定以及发现了我的建造模式中的实现出现了instantNoodleBuilder.cookNoodles().build()这样A.B.C的代码,之前在我们的设计模式开篇我们谈到了迪米特法则,迪米特法则有举例说不要出现A.B.C的情况,这里说明下迪米特法则的要求其实是不要出现依赖非朋友类的情况,其实实际上是不要出现A.getB.getC的情况,而这里其实每次返回的都是this,所以都是依赖的自己,并没有出现非朋友类,所以这样的写法没有违法迪米特法则。

六、参考

《设计模式之禅》

七、推荐阅读

Java设计模式之工厂方法模式与抽象工厂模式

Java设计模式之单例模式

JAVA设计模式之开篇

带你走进java集合之ArrayList

带你走进java集合之HashMap

Java锁之ReentrantLock(一)

Java锁之ReentrantLock(二)

Java锁之ReentrantReadWriteLock

© 著作权归作者所有

共有 人打赏支持
木木匠
粉丝 28
博文 21
码字总数 39854
作品 0
广州
高级程序员
私信 提问
设计模式 2014-12-19

book: 阎宏《JAVA与模式》 架构设计栏目 http://blog.csdn.net/enterprise/column.html 概要: http://bbs.csdn.net/forums/Embeddeddriver 23种设计模式分别是: 1.单例模式 2.工厂方法模式...

jayronwang
2014/12/19
0
0
设计模式15——Template Method设计模式

Template Method模板方法设计模式定义一个操作中算法的骨架,将具体步骤的执行延迟到子类中实现。Java中的抽象类就是使用了模板方法设计模式。模板方法设计模式结构如下: 以文档处理为例,T...

小米米儿小
2014/01/24
0
0
java 23种设计模式 深入理解

以下是学习过程中查询的资料,别人总结的资料,比较容易理解(站在各位巨人的肩膀上,望博主勿究) 创建型 抽象工厂模式 http://www.cnblogs.com/java-my-life/archive/2012/03/28/2418836.html ...

wc_飞豆
03/16
0
0
java中23种设计模式(上)

参考网址:http://blog.csdn.net/zhangerqing 资源:http://download.csdn.net/detail/zhangerqing/4835830 设计模式(Design Patterns) 设计模式(Design pattern)是一套被反复使用、多数...

青涩的梦
06/26
0
0
Java之美[从菜鸟到高手演变]之设计模式

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代...

只想一个人静一静
2014/02/25
0
2

没有更多内容

加载失败,请刷新页面

加载更多

前端工程师的知识体系

Front-End Engineer 这词很好的体现了前端的特(ku)殊(bi)性。 下图是张克军绘制的前端工程师图解: 前端开发的核心是HTML + CSS + JavaScript。本质上它们构成一个MVC框架,即HTML作为信息模...

文文1
6分钟前
0
0
随行付微服务测试之性能测试

背景 传统性能测试更多的是以事务为核心,更多的是由单个或者多个事务构成业务场景进行压测。全链路压测指完全引入相关联的系统,尽量真实模拟线上硬件环境,更多的是以请求为核心,完全模拟...

马力-随行付
8分钟前
0
0
JavaScript是如何工作的:事件循环和异步编程的崛起 + 5种使用 async/await 更好地编码方式!

摘要: 深度理解JS事件循环!!! 原文:JavaScript是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式! 作者:前端小智 Fundebug经授权转载,版权归原作者所有。...

Fundebug
9分钟前
0
0
hanlp在Python环境中的安装失败后的解决方法

Hanlp是由一系列模型与算法组成的javag工具包,目标是普及自然语言处理再生环境中的应用。有很多人在安装hanlp的时候会遇到安装失败的情况,下面就是某大神的分享的在python环境中安装失败的...

左手的倒影
30分钟前
1
0
【更新】Infragistics Ultimate UI for WPF v18.2(二):分类图

下载Infragistics Ultimate UI for WPF最新版本 Infragistics Ultimate UI for WPF是一款提供高速的网格和图表,轻松创建仿Office应用程序的WPF界面框架,从广度和深度两方面使得开发者在缩短...

电池盒
31分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部