Head First Design Mode(10)-模板方法模式
该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!
模板方法模式:
封装算法——好让子类在任何时候都可以将自己挂接进运算里;
星巴克场景举例:
咖啡制作:1煮水——>2冲泡——>3装杯——>4加糖;
茶制作:1煮水——>2浸泡——>3装杯——>4加柠檬;
泡的过程是星巴克的独门技艺……
Java语言的实现过程:
Coffee类和Tea类;
假设我们分别实现相应的类,那么各自将会有制作过程中的四个方法;相同的两个,和不同的两个;
没错我们发现了重复的代码;
我们将上述四个步骤放在一个接口中:prepareRecipe();
这个方法试图封装了咖啡或茶的制作过程;
我们试图通过继承来优化:
制作过程在每个类中表现不同,所以定义成抽象方法;
每个子类都实现自己的制作过程;
公共方法定义在超类中;
进一步的,我们注意到,用热水泡茶或咖啡实际上可以是一个方法,只不过应用在不同的饮料上;
类似的添加其他佐料也是一样的,因此,可以将制作的四个步骤统一定义在超类中,并重新命名;
这样制作过程就明确了;此外还有一些要明确的,我们来看下代码实现;
优化编码实现:
咖啡因饮料是一个抽象类;
需要子类各自处理的方法 brew()和addConditions()声明为抽象类;
prepareRecipe()方法被声明为final,因为这个发给嫩啊规定了星巴克的模板化制作过程,我们不希望子类覆盖这个方法;
Tea和Coffee类自行处理泡和添加的部分;
【code】
认识模板方法:
上边我们实现的就是模板方法模式;
prepareRecipe()是模板方法:这个方法用作了制作咖啡因饮料的算法模板;
在这个算法中,每一个步骤都被一个方法代表了;
有些方法被子类处理,需要子类提供的方法,必须在超类中声明为抽象;
模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现;
模板控制了算法,并让子类能提供某些步骤的实现;
子类只需调用模板方法即可实现制作;
或者说抽象的基类专注在算法本身,而由子类提供完成的实现;
定义模板方法模式:
模板方法模式 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以在不改变算法接口的情况下,重新定义算法中的某些步骤;
模板就是一个方法,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现;这可以确保算法的结构保持不变,同时由子类提供部分实现;
类图:
抽象类的定义:
对模板方法进行挂钩:
钩子是一种被声明在抽象类中的方法;
钩子的存在可以让子类有能力对算法的不同点进行挂钩;
要不要钩子,子类自行决定;
比如,我们可以加上一个if判断,而条件就是由一个钩子方法返回的bool值,以确定是否需要“外带”;
这样,子类就可以通过覆盖钩子方法来决定算法的执行流程;
更多关于钩子:
当子类必须提供算法中的某些步骤时,使用抽象方法;
如果算法部分是可选的,就使用钩子;
钩子可以让子类实现算法的可选部分,另一种用法是让子类能够有机会对模板中某些即将发生的步骤做出反应;
好莱坞原则:
别调用(打电话给)我们,我们会调用(打电话给)你;
这是一种防止“依赖腐败”的原则:
高层依赖底层,底层依赖高层,或者更加复杂的依赖关系存在时,没人懂得系统是如何设计的;
好莱坞原则下,允许底层组件将自己挂钩到系统上;
高层组件控制何时以及如何让底层组件参与,底层组件不可以直接调用高层组件;
对于咖啡因饮料的示例,我们可以让客户代码只依赖于抽象类,而不是具体的Tea或Coffee;
我们要尽可能的避免让高层和底层组件之间有明显的环状依赖;
模板方法:子类决定如何实现算法中的步骤;
策略:封装可互换的行为,然后使用委托来决定要采用哪个行为;
工厂方法:由子类决定实例化哪个具体类;
场景举例:
比如对于使用了泛型化对象的操作,如排序,就可以对泛型化的接口类型,在相应的方法中定义具体的排序规范(控制继承);
这样就可以针对实现了接口类型的对象进行相应的排序操作(具体类实现);
此时接口中定义的方法,实际都是一种模板方法(只不过不是典型的模板方法,但是符合模板方法模式的精神);
Java API中:
java.io的InputStream类的read()方法,是由子类实现,而这个方法又被read(byte b[], int off, int len)模板方法使用;
总结:
1.模板方法模式为我们提供了一种代码复用的重要技能;
2.模板方法的抽象类可以定义具体方法、抽象方法和钩子;
3.抽象方法有子类实现;
4.钩子是一种方法,他在抽下类中不做事或做默认的事,子类可以选择要不要去覆盖他;
5.为了防止子类改变模板方法中的算法,可以将模板方法声明为final;
6.好莱坞原则告诉我们,将决策权放在高层模块中,以便决定如何以及何时调用底层模块;
7.真实的编程环境中会有许多模板方法模式的变体,别期望都被认出来;
8.策略模式用组合,模板方法模式用继承,二者都封装算法;
9.工厂方法是模板方法的一种特殊版本;
OO基础:
抽象;
封装
继承;
多态;
OO原则:
封装变化
多用组合,少用继承
针对接口编程,不针对实现编程
为交互对象之间的松耦合设计而努力;
类应该对扩展开放,对修改关闭;
依赖抽象,不要依赖具体类;
只和朋友交谈(最少知识原则);
——别找我,我会找你(好莱坞原则:由超类主控一切,需要的时候自然会去调用子类);
OO模式:
策略模式:定义算法族,分别封装起来,让他们之间互相替换,此模式让算法的变化独立于使用算法的客户;
观察者模式:在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新;
装饰者模式:动态地将责任附加到对象上;想要扩展功能,装饰者提供有别于继承的另一种选择;
简单工厂模式;
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个;工厂方法让类把实例化推迟到子类;
抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确具体的类;
单件模式:确保一个类只有一个实例,并提供全局访问点;
命令模式:将请求封装成对象,这可以让你使用不同的请求,队列或者日志请求来参数化其他对象;命令模式也支持撤销操作;
适配器模式:将一个类的接口转换成客户期待的另一个接口,适配器让原来不兼容的类可以合作无间;
外观模式:提供了一个统一的接口,用来访问子系统中的一群接口;外观模式定义了高层接口,让子系统更容易使用;
——模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中;模板方法使得子类可以在不改变算法结构的情况下,重新定义/捕获算法中的某些步骤;