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

你可能感兴趣的:(st,设计模式学习笔记8,HeadFir)