模板方法模式(封装算法)

哎,在需要进入这个洞之前他原本是个好老板的,结构这“全部”都变成“我的”工作了。你懂我的意思吧?他根本就不见人影。

书中通过泡咖啡和泡茶的例子,我们已经明白两种冲泡法是基本相同的,只是一些步骤需要不同的实现。所以我们泛化了冲泡法,把它放在基类。一些步骤依赖子类进行。

 认识模板方法

 abstract class CaffeineBeverage{
        //模板方法,定义步骤
        public void prepareRecipe(){
            boilWater();
            brew();
            pourInCup();
            addConditiments();
        }

        public abstract void brew();
        public abstract void addConditiments();

        public void boilWater(){
            Console.WriteLine("boilWater");
        }
        public void pourInCup(){
            Console.WriteLine("pourInCup");
        }
    }
prepareRecipe()是我们的模板方法:

1,它是一个方法;

2,它用作一个算法的模板,在这个例子中,算法是用来制作咖啡因饮料的。


在这个模板中,算法内的每一个步骤都被一个方法代表了。某些方法是由这个类(也就是超类)处理的。。。,某些方法则是由子类处理的。

需要由子类提供的方法,必须在超类中声明为抽象。


模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。

模板方法模式的正式定义,在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

类图如下:



引入钩子

  abstract class AbstractClass
        {
            void templateMethod()
            {
                primitiveOperation1();
                primitiveOperation2();
                concreateOperation();
                hook();
            }
            abstract void primitiveOperation1();
            abstract void primitiveOperation2();
            void concreateOperation() { 添加实现}
            void hook(){}
        }

1,钩子是一种被声明在抽象类中的方法,但是有空的或者默认的实现。钩子的存在可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,让子类决定

钩子的作用,看看下面的代码

    abstract class CaffeineBeverage{
        public void prepareRecipe(){
            boilWater();
            brew();
            pourInCup();
            if (customerWantsCondiments()){
                addConditiments();
            }
        }
        public abstract void brew();
        public abstract void addConditiments();
        public void boilWater(){
            Console.WriteLine("boilWater");
        }
        public void pourInCup(){
            Console.WriteLine("pourInCup");
        }
        //定义空的缺省实现
        //子类可以覆盖这个方法,但不一定见得要这么做
        public virtual Boolean customerWantsCondiments(){
            return true;
        }
    }
钩子竟然能够作为条件控制,影响抽象类中的算法流程。


抽象方法和钩子的选择

当子类“必须”提供算法中的某些方法或步骤时,就是用抽象方法。

    如果算法的这个部分是可选的,就用钩子

    如果是钩子的话,子类就可以选择实现这个钩子,但是并不强制这么做,应为提供默认的实现了。


使用钩子的作用

钩子可以让子类实现算法中的可选部分。

当钩子对于子类的实现并不重要的时候,子类可以对钩子置之不理

子类能够有激活对模板方法的某些即将发生的步骤做出反应。


注意,由于子类必须实现抽象类中的所有的抽象方法,所以应该保持抽象方法的数目越少越好,这就需要你在设计的时候对算法内的数据不要切割太细,同时粒度太大的程序弹性又受到限制,这就需要你去权衡了,程序设计中太多的地方是平衡和妥协了。


好莱坞原则

别调用(打电话给)我们,我们会调用(打电话给)你。


在这个原则之下,我们允许底层组件将自己挂钩到系统上,而高层组件会决定什么时候和如何使用这些底层组件,我们把决策权放到高层模块中。也就是说,高层组件对待底层组件的方式就是“别调用(打电话给)我们,我们会调用(打电话给)你”。

这个原则和依赖倒置原则的关系式,后者交给我们如何尽量避免使用具体类,而多使用抽象,注重体现如何避免依赖。前者则是通过创建框架和组件的一种技巧。注重创建一个有弹性的设计,同时又防止其他类太过依赖他们。


看看模式方法的一些应用

java.io的InputStream类有一个read()方法,是由子类实现的,而这个方法又会被read(byte b[], int off, int len)模板方法使用。Activity的oncreate()。。onDestory()等.

数组排序方法sort(),这是一个模板方法的变体。

sort()的设计者希望这个方法能适用于所有的数组,所以将其设置为静态方法。而同时你必须实现compartTo方法后才能使用sort方法,为了达到这一点,设计者利用了Comparable接口,提供这个接口所声明的方法,也就是compartTo。

排序的实现实际上看起来更像是策略模式,而不是模板方法模式。为什么我买要将它归为模板方法?

之所以这么认为,可能是因为策略模式使用对象组合。在某种程度上,你是对的-----我们使用数组对象排序我们的数组,这部分和策略模式非常相似。但是请记住,在策略模式中,你所组合的类实现了整个算法。数组所实现的排序算法并不完整,它需要一个类填补compareTo()方法的实现。因此,我们认为更像模板方法。


最后,我们比较一下模板方法模式和策略模式的异同

模板方法使用的继承,所以复用了很多的代码,更有效率一点。策略模式通过对象的组合的方式,让可以选择算法的实现,更有弹性。

前者定义了一个算法的大纲,由子类(注意这里是继承)定义其中的某些步骤的内容,这样的算法的细节可有不同,但是算法的结构和流程保持不变,它对算法有更多的控制权。而后者定义了一个算法的家族,并让这些算法互换,正因为每一个算法都被封装了起来,所以客户可以在运行时改变他们的算法,轻易的使用不同的算法(不是通过继承而是接口的组合),它则更有弹性。


要点:

1,“模板方法”定义了算法的步骤,把这些步骤的实现延迟到子类。

2,模板方法模式为我们提供了一种代码复用的重要技巧。

3,模板方法的抽象类可以定义具体的方法,抽象方法和钩子。

4,抽象方法由子类实现。

5,钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要覆盖它。

6,为了防止子类改变模板方法中的算法,可以将模板方法声明为final

7,好莱坞原则告诉我们,将决策权放在高层的模块中,以便决定如何以及何时调用底层模块。

8,你将在真实世界代码中看到模板方法模式的许多变体,不要期待他们全都是一眼就可以被你认出来的。

9,策略模式和模板方法模式都封装算法,一个用组合,一个用继承

10,工厂方法是模板方法的一种特殊版本。



参考http://zhangjunhd.blog.51cto.com/113473/412996

http://blog.youkuaiyun.com/gnuhpc/article/details/5453858


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值