HeadFir st 设计模式学习笔记8--模板方法模式
1.这个模式比较简单,我们举⼀个银行贷款申请流程程序的例子(这个并非是书中的例子):
申请贷款,银行要检查这个客户的⼀些事宜,譬如客户收支状况记录、从三个地方拿到他的信
用记录、得到其他已有相关债务信息、得到借债人股票市值、得到借债人未来收入预期分析等
等。我们可以设计如下⼀个模板方法:
abstract class CheckBackground {
public abstract void checkBank();
public abstract void checkCredit();
public abstract void checkLoan();
public abstract void checkStock();
public abstract void checkIncome();
public void check() {//模板方法为非抽象的,可以设置为final,这样子类就不会修改这个流程了。
checkBank();
checkCredit();
checkLoan();
checkStock();
checkIncome();
}
}
我们的贷款类就可以如下这么设计了,实现了模板方法中的抽象方法:
class LoanApp extends CheckBackground {
private String name;
public LoanApp(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void checkBank() {
//ck acct, balance
System.out.println("check bank...");
}
public void checkCredit() {
//ck score from 3 companies
System.out.println("check credit...");
}
public void checkLoan() {
//ck other loan info
System.out.println("check other loan...");
}
public void checkStock() {
//ck how many stock values
System.out.println("check stock values...");
}
public void checkIncome() {
//ck how much a family make
System.out.println("check family income...");
}
//other methods
}
2.这个模式的核心思想是“抽象”。首先作为模板方法,它必须是⼀个方法(废话……),它可以
作为⼀个算法的例子,在模板方法中,每⼀个步骤都要由另⼀个方法来完成,某些方法是由这
个类完成,某些则是由这个类的子类完成(需要由子类提供的方法必须在超类中声明为抽象)
——模板方法定义了⼀个算法的步骤,并允许子类为⼀个或多个步骤提供实现,实质上模板方
法提供了⼀个算法的框架(Framework),步骤定死,而实现又子类进行。这个模式的优势在
于最大限度的发挥抽象能力,保证程序需求变更或者优化升级时必须进行的变更最小。注意、
包含模板方法的类本身要是抽象的。
3.我们能不能比较智能的控制算法呢,比如某些时候这个客户是⼀个VI P客户,我们不需要对其I
n come去进行检查,此时可以使用Hook(挂钩),在需要的时候把⼀些方法“挂上”。我们这样设
计这个模板方法类:
abst ract cl ass Ch eckBackgrou n d {
pu bl i c abst ract v oi d ch eckBan k();
pu bl i c abst ract v oi d ch eckCredi t ();
pu bl i c abst ract v oi d ch eckLoan ();
pu bl i c abst ract v oi d ch eckStock();
pu bl i c abst ract v oi d ch eckI n come();
publ ic void check() {/ /模板方法为非抽象的
ch eckBan k();
ch eckCredi t ();
ch eckLoan ();
i f(! ischeckVIP){/ /使用钩子,子类可以根据需要进行覆盖
ch eckStock();
}
ch eckI n come();
}
publ ic boolean ischeckVIP(){/ /钩子函数
return false;
}
}
挂钩函数除了可以让子类控制⼀些算法的流程以外,另⼀个功能是让子类有机会对模板方法中
某些即将发生的步骤做出反应。
注意,由于子类必须实现抽象类中的所有抽象方法,所以应该保持抽象方法的数目越少越好,
这就需要你在设计的时候对算法内的数据不要切割太细,同时粒度太大程序的弹性又受到限制
,这就需要你去权衡了,程序设计中太多的地方是平衡和妥协了。
4.我们此时引出⼀个新的OO原则——好莱坞原则:别电话给我们,我们会打电话给你——别调
用我们,我们会去调用你。
在这个原则之下,我们允许低层组件将自己挂钩到系统上,而高层组件会决定什么时候和如何
使用这些低层组件,我们把决策权放到高层模块中。也就是说,高层组件对待低层组件的方式
就是上边那句黑体字的原则。书中举了⼀个咖啡与茶的例子(有趣的是,《咖啡与茶》是丁薇
早年间的⼀张唱片的名字,我高⼀的时候买到了正版卡带)来解释模板方法模式与这个原则关
系,见原书(英文版)P297。这个原则和依赖倒置原则的关系是,后者交给我们如何尽量避免
使用具体类,而多使用抽象,注重体现如何避免依赖。前者则是通过创建框架和组件的⼀种技
巧,注重创建⼀个有弹性的设计,同时又防止其他类太过依赖它们,避免类的环状依赖。值得
注意的是,工厂方法是模板方法的⼀种特殊版本。
5.接着,我们看看Jav a中有什么是使用了这个模式的:数组排序方法sort ( )
sort ()的设计者希望这个方法能适用于所有的数组,所以将其设置为静态方法。而同时你必须实
现compartT o方法后才能使用sort方法,为了达到这⼀点,设计者利用了Comparabl e接口,提供
这个接口所声明的方法,也就是compartT o。
我们现在利用这个接口去设计⼀个鸭子的类,试图根据体重去对鸭子进行比较。
pu bl i c cl ass Du ck i mpl emen t s Comparabl e {
St ri n g n ame;
i n t wei gh t ;
pu bl i c Du ck(St ri n g n ame, i n t wei gh t ) {
th i s.n ame = n ame;
th i s.wei gh t = wei gh t ;
}
pu bl i c St ri n g toSt ri n g() {
retu rn n ame + " wei gh s " + wei gh t ;
}
pu bl i c i n t compareT o(Obj ect obj ect ) {
Du ck oth erDu ck = (Du ck)obj ect ;
i f (th i s.wei gh t < oth erDu ck.wei gh t ) {
retu rn -1;
} el se i f (th i s.wei gh t == oth erDu ck.wei gh t ) {
retu rn 0;
} el se { / / th i s.wei gh t > oth erDu ck.wei gh t
retu rn 1;
}
}
}
这样,我们就能直接使用数组中的sort方法了:
i mport j av a.u t i l .Array Li st ;
i mport j av a.u t i l .Array s;
pu bl i c cl ass Du ckSortT estDri v e {
pu bl i c stat i c v oi d mai n (St ri n g[ ] args) {
Du ck[ ] du cks = {
n ew Du ck("Daf f y ", 8),
n ew Du ck("Dewey ", 2),
n ew Du ck("Howard", 7),
n ew Du ck("Lou i e", 2),
n ew Du ck("Don al d", 10),
n ew Du ck("Hu ey ", 2)
};
Sy stem.ou t .pri n t l n ("Before sort i n g: ");
di spl ay (du cks);
Array s. sort (du cks);
Sy stem.ou t .pri n t l n ("/n Af ter sort i n g: ");
di spl ay (du cks);
}
pu bl i c stat i c v oi d di spl ay (Du ck[ ] du cks) {
for (i n t i = 0; i < du cks. l en gth ; i ++) {
Sy stem.ou t .pri n t l n (du cks[ i ] );
}
}
}
这和模板方法又有什么关系呢?
完成Comparabl e接口的compareT o方法使得元素自行提供比较大小的算法部分。这就有点像前
边我们的贷款程序中自己定义如何检查客户收入的函数⼀样。
我们再举两个例子:Swi n g:
i mport j av a.awt . *;
i mport j av ax . swi n g. *;
pu bl i c cl ass My Frame ex ten ds JFrame {
pu bl i c My Frame(St ri n g t i t l e) {
su per(t i t l e);
th i s. setDefau l tCl oseOperat i on (JFrame.EXI T_ON_CLOSE);
th i s. setSi z e(300,300);
th i s. setVi si bl e(t ru e);
}
pu bl i c v oi d pai n t (Graph i cs graph i cs) {/ /默认情况下,它什么都不做,是⼀个挂钩,通过覆盖该
方法,你可以将自己的代码插入JFrame中,
su per .pai n t (graph i cs);
St ri n g msg = "I ru l e! ! ";
graph i cs.drawSt ri n g(msg, 100, 100);
}
pu bl i c stat i c v oi d mai n (St ri n g[ ] args) {
My Frame my Frame = n ew My Frame("Head Fi rst Desi gn Pat tern s");
}
}
Appl et:具体的appl et大量使用挂钩函数提供行为。
i mport j av a.appl et .Appl et ;
i mport j av a.awt .Graph i cs;
pu bl i c cl ass My Appl et ex ten ds Appl et {
St ri n g message;
pu bl i c v oi d i n i t () {
message = "Hel l o Worl d, I 'm al i v e! ";
repai n t ();
}
pu bl i c v oi d start () {
message = "Now I 'm start i n g u p. . . ";
repai n t ();
}
pu bl i c v oi d stop() {
message = "Oh , n ow I 'm bei n g stopped. . . ";
repai n t ();
}
pu bl i c v oi d dest roy () {
message = "Goodby e, cru el worl d";
repai n t ();
}
pu bl i c v oi d pai n t (Graph i cs g) {
g.drawSt ri n g(message, 5, 15);
}
}
6.最后,我们比较⼀下模板方法模式和策略模式的异同。
前者定义⼀个算法的大纲,由子类(注意这里是继承)定义其中的某些步骤的内容,这样算法
的细节可有不同,但是算法的结构和流程保持不变,它对算法有更多的控制权。而后者定义了
⼀个算法的家族,并让这些算法互换,正因为每⼀个算法都被封装了起来,所以客户可以轻易
地使用不同的算法(不是通过继承而是接口的组合),它则更有弹性。
在线视频:h t tp: / / v . y ou ku . com/ v _sh ow/ i d_XMj U2Nz A0OTI 4.h tml
HeadFir st 设计模式学习笔记8--模板方法模式相关推荐
- HeadFir st 设计模式学习笔记21-- 解释者(Inter pr eter)模式拾零
HeadFir st 设计模式学习笔记21-- 解释者(Inter pr eter)模式拾零 1.概述 这个模式是在不能叫做模式,它的作用是实现⼀种语言规范的解释器,比如罗马数字解释器. 2.实例 我 ...
- 设计模式学习笔记——模板(Template)模式
设计模式学习笔记--模板(Template)模式 @(设计模式)[设计模式, 模板模式, template, 模板方法] 设计模式学习笔记模板Template模式 基本介绍 模板案例 类图 实现代码 ...
- 设计模式学习笔记——解释器(Interpreter)模式
设计模式学习笔记--解释器(Interpreter)模式 @(设计模式)[设计模式, 解释器模式, Interpreter] 设计模式学习笔记解释器Interpreter模式 基本介绍 解释器案例 类 ...
- 设计模式学习笔记——命令(Command)模式
设计模式学习笔记--命令(Command)模式 @(设计模式)[设计模式, 命令模式, command] 设计模式学习笔记命令Command模式 基本介绍 命令案例 类图 实现代码 Command接口 ...
- 设计模式学习笔记——代理(Proxy)模式
设计模式学习笔记--代理(Proxy)模式 @(设计模式)[设计模式, 代理模式, proxy] 设计模式学习笔记代理Proxy模式 基本介绍 代理案例 类图 实现代码 Printable接口 Pri ...
- 设计模式学习笔记——状态(State)模式框架
设计模式学习笔记--状态(State)模式框架 @(设计模式)[设计模式, 状态模式, State] 设计模式学习笔记状态State模式框架 基本介绍 状态案例 类图 实现代码 State接口 Day ...
- 设计模式学习笔记——备忘录(Memento)模式
设计模式学习笔记--备忘录(Memento)模式 @(设计模式)[设计模式, 备忘录模式, memento] 设计模式学习笔记备忘录Memento模式 基本介绍 备忘录案例 类图 实现代码 Memen ...
- 设计模式学习笔记——观察者(Observer)模式
设计模式学习笔记--观察者(Observer)模式 @(设计模式)[设计模式, 观察者模式, Observer] 设计模式学习笔记观察者Observer模式 基本介绍 观察者案例 类图 实现代码 Ob ...
- 设计模式学习笔记——外观(Facade)模式
设计模式学习笔记--外观(Facade)模式 @(设计模式)[设计模式, 外观模式, facade] 设计模式学习笔记外观Facade模式 基本介绍 外观案例 类图 实现代码 Database类 ma ...
最新文章
- NSMutableParagraphStyle /NSParagraphStyle
- 前后端分离开发,日志应该如何进行记录,在出现问题的时候,方便定位问题
- Mybatis Plus——以XML方式使用 Wrapper 自定义SQL时IDEA错误[**expected, got ‘${‘]解决方案
- eclipse 面包屑开关 / 查看class再哪个jar中
- Spring 自动装配模式之byType
- 的主机名_如何在Mac 上更改电脑的名称或本地局域网主机名?
- 2020中国奢侈品消费者数字行为洞察报告
- 删除链表的倒数第n个节点 python_LeetCode 19.删除链表的倒数第N个节点(Python)
- mysql手工注入imformation_mysql 简单手工注入
- Oracle插入时间
- C++ 构造函数与析构函数
- 8 月社群专属福利活动开启!进群免费领取开发视频课程!
- 海康威视:工程项目不是我们的目标,对创新业务发展充满信心...
- springboot-day01-引入基础
- 读君山-七年阿里老人谈新人程序员的成长
- 每日算法系列【LeetCode 684】冗余连接
- Codeforces Round #381 (Div. 2) D. Alyona and a tree 树上二分+前缀和思想
- mysql修改有外键约束的表结构
- eclipse中出现代码覆盖的颜色信息,如何去掉
- PWA 应用列表及常用工具