模板方法模式

在面向對象程序設計過程中笤受,程序員常常會遇到這種情況:設計一個系統(tǒng)時知道了算法所需的關鍵步驟壁公,而且確定了這些步驟的執(zhí)行順序,但某些步驟的具體實現(xiàn)還未知紊册,或者說某些步驟的實現(xiàn)與具體的環(huán)境相關快耿。

例如,去銀行辦理業(yè)務一般要經過以下4個流程:取號掀亥、排隊、辦理具體業(yè)務搪花、對銀行工作人員進行評分等,其中取號吮便、排隊和對銀行工作人員進行評分的業(yè)務對每個客戶是一樣的,可以在父類中實現(xiàn)髓需,但是辦理具體業(yè)務卻因人而異房蝉,它可能是存款僚匆、取款或者轉賬等搭幻,可以延遲到子類中實現(xiàn)。

這樣的例子在生活中還有很多檀蹋,例如,一個人每天會起床攻臀、吃飯、做事刨啸、睡覺等识脆,其中“做事”的內容每天可能不同设联。我們把這些規(guī)定了流程或格式的實例定義成模板,允許使用者根據自己的需求去更新它换团,例如,簡歷模板艘包、論文模板耀盗、Word 中模板文件等想虎。

以下介紹的模板方法模式將解決以上類似的問題叛拷。

模式的定義與特點

模板方法(Template Method)模式的定義如下:定義一個操作中的算法骨架,而將算法的一些步驟延遲到子類中忿薇,使得子類可以不改變該算法結構的情況下重定義該算法的某些特定步驟。它是一種類行為型模式署浩。

該模式的主要優(yōu)點如下。

  1. 它封裝了不變部分你雌,擴展可變部分二汛。它把認為是不變部分的算法封裝到父類中實現(xiàn)婿崭,而把可變部分算法由子類繼承實現(xiàn)肴颊,便于子類繼續(xù)擴展。
  2. 它在父類中提取了公共的部分代碼婿着,便于代碼復用。
  3. 部分方法是由子類實現(xiàn)的提完,因此子類可以通過擴展方式增加相應的功能,符合開閉原則徒欣。

該模式的主要缺點如下。

  1. 對每個不同的實現(xiàn)都需要定義一個子類打肝,這會導致類的個數(shù)增加,系統(tǒng)更加龐大粗梭,設計也更加抽象。
  2. 父類中的抽象方法由子類實現(xiàn)断医,子類執(zhí)行的結果會影響父類的結果,這導致一種反向的控制結構酷宵,它提高了代碼閱讀的難度。

模式的結構與實現(xiàn)

模板方法模式需要注意抽象類與具體子類之間的協(xié)作。它用到了虛函數(shù)的多態(tài)性技術以及“不用調用我炕置,讓我來調用你”的反向控制技術。現(xiàn)在來介紹它們的基本結構朴摊。

1. 模式的結構

模板方法模式包含以下主要角色。

(1) 抽象類(Abstract Class):負責給出一個算法的輪廓和骨架口锭。它由一個模板方法和若干個基本方法構成。這些方法的定義如下鹃操。

① 模板方法:定義了算法的骨架春哨,按某種順序調用其包含的基本方法荆隘。

② 基本方法:是整個算法中的一個步驟赴背,包含以下幾種類型。

  • 抽象方法:在抽象類中申明凰荚,由具體子類實現(xiàn)。
  • 具體方法:在抽象類中已經實現(xiàn)缆毁,在具體子類中可以繼承或重寫它。
  • 鉤子方法:在抽象類中已經實現(xiàn)积锅,包括用于判斷的邏輯方法和需要子類重寫的空方法兩種。

(2) 具體子類(Concrete Class):實現(xiàn)抽象類中所定義的抽象方法和鉤子方法缚陷,它們是一個頂級邏輯的一個組成步驟。

模板方法模式的結構圖如圖 1 所示箫爷。

模板方法模式的結構圖

圖1 模板方法模式的結構圖

2. 模式的實現(xiàn)

模板方法模式的代碼如下:


1.  package  templateMethod;
2.  public class  TemplateMethodPattern
3.  {
4.  public static void main(String[] args)
5.  {
6.  AbstractClass  tm=new ConcreteClass();
7.  tm.TemplateMethod();
8.  }
9.  }
10.  //抽象類
11.  abstract class  AbstractClass
12.  {
13.  public void TemplateMethod() //模板方法
14.  {
15.  SpecificMethod();
16.  abstractMethod1();          
17.  abstractMethod2();
18.  }  
19.  public void SpecificMethod() //具體方法
20.  {
21.  System.out.println("抽象類中的具體方法被調用...");
22.  }   
23.  public abstract void abstractMethod1(); //抽象方法1
24.  public abstract void abstractMethod2(); //抽象方法2
25.  }
26.  //具體子類
27.  class  ConcreteClass extends AbstractClass
28.  {
29.  public void abstractMethod1()
30.  {
31.  System.out.println("抽象方法1的實現(xiàn)被調用...");
32.  }   
33.  public void abstractMethod2()
34.  {
35.  System.out.println("抽象方法2的實現(xiàn)被調用...");
36.  }
37.  }

程序的運行結果如下:

<pre class="info-box">抽象類中的具體方法被調用...
抽象方法1的實現(xiàn)被調用...
抽象方法2的實現(xiàn)被調用...</pre>

模式的應用實例

【例1】用模板方法模式實現(xiàn)出國留學手續(xù)設計程序虎锚。

分析:出國留學手續(xù)一般經過以下流程:索取學校資料硫痰,提出入學申請窜护,辦理因私出國護照、出境卡和公證柱徙,申請簽證,體檢敌完、訂機票、準備行裝滨溉,抵達目標學校等长赞,其中有些業(yè)務對各個學校是一樣的晦攒,但有些業(yè)務因學校不同而不同涧卵,所以比較適合用模板方法模式來實現(xiàn)。

在本實例中柳恐,我們先定義一個出國留學的抽象類 StudyAbroad,里面包含了一個模板方法 TemplateMethod()乐设,該方法中包含了辦理出國留學手續(xù)流程中的各個基本方法,其中有些方法的處理由于各國都一樣蠕啄,所以在抽象類中就可以實現(xiàn),但有些方法的處理各國是不同的歼跟,必須在其具體子類(如美國留學類 StudyInAmerica)中實現(xiàn)。如果再增加一個國家哈街,只要增加一個子類就可以了,圖 2 所示是其結構圖骚秦。

出國留學手續(xù)設計程序的結構圖

圖2 出國留學手續(xù)設計程序的結構圖

程序代碼如下:


1.  package  templateMethod;
2.  public class  StudyAbroadProcess
3.  {
4.  public static void main(String[] args)
5.  {
6.  StudyAbroad  tm=new StudyInAmerica();
7.  tm.TemplateMethod();
8.  }
9.  }
10.  //抽象類: 出國留學
11.  abstract class  StudyAbroad
12.  {
13.  public void TemplateMethod() //模板方法
14.  {
15.  LookingForSchool(); //索取學校資料
16.  ApplyForEnrol();    //入學申請 
17.  ApplyForPassport(); //辦理因私出國護照、出境卡和公證
18.  ApplyForVisa();     //申請簽證
19.  ReadyGoAbroad();    //體檢硬梁、訂機票胞得、準備行裝
20.  Arriving();         //抵達
21.  }              
22.  public void ApplyForPassport()
23.  {
24.  System.out.println("三.辦理因私出國護照荧止、出境卡和公證:");
25.  System.out.println("  1)持錄取通知書阶剑、本人戶口簿或身份證向戶口所在地公安機關申請辦理因私出國護照和出境卡。");
26.  System.out.println("  2)辦理出生公證書个扰,學歷葱色、學位和成績公證,經歷證書苍狰,親屬關系公證,經濟擔保公證淋昭。");
27.  }
28.  public void ApplyForVisa()
29.  {
30.  System.out.println("四.申請簽證:");
31.  System.out.println("  1)準備申請國外境簽證所需的各種資料,包括個人學歷英融、成績單、工作經歷的證明驶悟;個人及家庭收入、資金和財產證明痕鳍;家庭成員的關系證明等;");
32.  System.out.println("  2)向擬留學國家駐華使(領)館申請入境簽證笼呆。申請時需按要求填寫有關表格,遞交必需的證明材料诗赌,繳納簽證。有的國家(比如美國境肾、英國、加拿大等)在申請簽證時會要求申請人前往使(領)館進行面試偶宫。");
33.  }
34.  public void ReadyGoAbroad()
35.  {
36.  System.out.println("五.體檢、訂機票纯趋、準備行裝:");
37.  System.out.println("  1)進行身體檢查冷离、免疫檢查和接種傳染病疫苗吵冒;");
38.  System.out.println("  2)確定機票時間西剥、航班和轉機地點。");
39.  }
40.  public abstract void LookingForSchool();//索取學校資料
41.  public abstract void ApplyForEnrol();   //入學申請
42.  public abstract void Arriving();        //抵達
43.  }
44.  //具體子類: 美國留學
45.  class  StudyInAmerica extends StudyAbroad
46.  {
47.  @Override
48.  public void LookingForSchool()
49.  {
50.  System.out.println("一.索取學校以下資料:");
51.  System.out.println("  1)對留學意向國家的政治瞭空、經濟、文化背景和教育體制南捂、學術水平進行較為全面的了解;");
52.  System.out.println("  2)全面了解和掌握國外學校的情況溺健,包括歷史、學費鞭缭、學制魏颓、專業(yè)缚去、師資配備琼开、教學設施、學術地位、學生人數(shù)等躏精;");
53.  System.out.println("  3)了解該學校的住宿、交通矗烛、醫(yī)療保險情況如何箩溃;");
54.  System.out.println("  4)該學校在中國是否有授權代理招生的留學中介公司瞭吃?");
55.  System.out.println("  5)掌握留學簽證情況涣旨;");
56.  System.out.println("  6)該國政府是否允許留學生合法打工?");
57.  System.out.println("  8)畢業(yè)之后可否移民霹陡?");
58.  System.out.println("  9)文憑是否受到我國認可?");
59.  }
60.  @Override
61.  public void ApplyForEnrol()
62.  {
63.  System.out.println("二.入學申請:");
64.  System.out.println("  1)填寫報名表攒霹;");
65.  System.out.println("  2)將報名表、個人學歷證明催束、最近的學習成績單伏社、推薦信泣崩、個人簡歷洛口、托缚Γ或雅思語言考試成績單等資料寄往所申請的學校;");
66.  System.out.println("  3)為了給簽證辦理留有充裕的時間妨马,建議越早申請越好,一般提前1年就比較從容烘跺。");       
67.  }
68.  @Override
69.  public void Arriving()
70.  {
71.  System.out.println("六.抵達目標學校:");
72.  System.out.println("  1)安排住宿;");
73.  System.out.println("  2)了解校園及周邊環(huán)境滤淳。");
74.  }
75.  }

程序的運行結果如下:

<pre class="info-box">一.索取學校以下資料:
1)對留學意向國家的政治、經濟铺敌、文化背景和教育體制汇歹、學術水平進行較為全面的了解偿凭;
2)全面了解和掌握國外學校的情況,包括歷史弯囊、學費系忙、學制、專業(yè)、師資配備商蕴、教學設施、學術地位吼过、學生人數(shù)等;
3)了解該學校的住宿盗忱、交通、醫(yī)療保險情況如何扇谣;
4)該學校在中國是否有授權代理招生的留學中介公司?
5)掌握留學簽證情況罐寨;
6)該國政府是否允許留學生合法打工序矩?
8)畢業(yè)之后可否移民鸯绿?
9)文憑是否受到我國認可簸淀?
二.入學申請:
1)填寫報名表;
2)將報名表租幕、個人學歷證明、最近的學習成績單劲绪、推薦信盆赤、個人簡歷蝎宇、托福或雅思語言考試成績單等資料寄往所申請的學校姥芥;
3)為了給簽證辦理留有充裕的時間,建議越早申請越好凉唐,一般提前1年就比較從容。
三.辦理因私出國護照台囱、出境卡和公證:
1)持錄取通知書、本人戶口簿或身份證向戶口所在地公安機關申請辦理因私出國護照和出境卡咱娶。
2)辦理出生公證書,學歷膘侮、學位和成績公證,經歷證書琼了,親屬關系公證夫晌,經濟擔保公證雕薪。
四.申請簽證:
1)準備申請國外境簽證所需的各種資料晓淀,包括個人學歷、成績單凶掰、工作經歷的證明;個人及家庭收入锄俄、資金和財產證明勺拣;家庭成員的關系證明等;
2)向擬留學國家駐華使(領)館申請入境簽證药有。申請時需按要求填寫有關表格苹丸,遞交必需的證明材料苇经,繳納簽證。有的國家(比如美國扇单、英國、加拿大等)在申請簽證時會要求申請人前往使(領)館進行面試蜘澜。
五.體檢、訂機票鄙信、準備行裝:
1)進行身體檢查、免疫檢查和接種傳染病疫苗银受;
2)確定機票時間、航班和轉機地點宾巍。
六.抵達目標學校:
1)安排住宿赖淤;
2)了解校園及周邊環(huán)境蜀漆。</pre>

模式的應用場景

模板方法模式通常適用于以下場景咱旱。

  1. 算法的整體步驟很固定,但其中個別部分易變時吐限,這時候可以使用模板方法模式,將容易變的部分抽象出來诸典,供子類實現(xiàn)描函。
  2. 當多個子類存在公共的行為時狐粱,可以將其提取出來并集中到一個公共父類中以避免代碼重復。首先肌蜻,要識別現(xiàn)有代碼中的不同之處,并且將不同之處分離為新的操作蒋搜。最后判莉,用一個調用這些新的操作的模板方法來替換這些不同的代碼育谬。
  3. 當需要控制子類的擴展時,模板方法只在特定點調用鉤子操作膛檀,這樣就只允許在這些點進行擴展。

模式的擴展

在模板方法模式中宿刮,基本方法包含:抽象方法、具體方法和鉤子方法胡桃,正確使用“鉤子方法”可以使得子類控制父類的行為。如下面例子中翠胰,可以通過在具體子類中重寫鉤子方法 HookMethod1() 和 HookMethod2() 來改變抽象父類中的運行結果自脯,其結構圖如圖 3 所示之景。

含鉤子方法的模板方法模式的結構圖

圖3 含鉤子方法的模板方法模式的結構圖

程序代碼如下:


1.  package  templateMethod;
2.  public class  HookTemplateMethod
3.  {
4.  public static void main(String[] args)
5.  {
6.  HookAbstractClass  tm=new HookConcreteClass();
7.  tm.TemplateMethod();
8.  }
9.  }
10.  //含鉤子方法的抽象類
11.  abstract class  HookAbstractClass
12.  {
13.  public void TemplateMethod() //模板方法
14.  {
15.  abstractMethod1();
16.  HookMethod1();
17.  if(HookMethod2())
18.  {
19.  SpecificMethod();   
20.  }
21.  abstractMethod2();
22.  }  
23.  public void SpecificMethod() //具體方法
24.  {
25.  System.out.println("抽象類中的具體方法被調用...");
26.  }
27.  public void HookMethod1(){}  //鉤子方法1
28.  public boolean  HookMethod2() //鉤子方法2
29.  {
30.  return true;
31.  }
32.  public abstract void abstractMethod1(); //抽象方法1
33.  public abstract void abstractMethod2(); //抽象方法2
34.  }
35.  //含鉤子方法的具體子類
36.  class  HookConcreteClass extends HookAbstractClass
37.  {
38.  public void abstractMethod1()
39.  {
40.  System.out.println("抽象方法1的實現(xiàn)被調用...");
41.  }   
42.  public void abstractMethod2()
43.  {
44.  System.out.println("抽象方法2的實現(xiàn)被調用...");
45.  }   
46.  public void HookMethod1()
47.  {
48.  System.out.println("鉤子方法1被重寫...");
49.  }
50.  public boolean  HookMethod2()
51.  {
52.  return false;
53.  }
54.  }

程序的運行結果如下:

<pre class="info-box">抽象方法1的實現(xiàn)被調用...
鉤子方法1被重寫...
抽象方法2的實現(xiàn)被調用...</pre>

如果鉤子方法 HookMethod1() 和鉤子方法 HookMethod2() 的代碼改變锻狗,則程序的運行結果也會改變焕参。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市叠纷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涩嚣,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顷歌,死亡現(xiàn)場離奇詭異幔睬,居然都是意外死亡眯漩,警方通過查閱死者的電腦和手機溪窒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摹芙,“玉大人,你說我怎么就攤上這事浮禾。” “怎么了盈电?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵杯活,是天一觀的道長。 經常有香客問我旁钧,道長,這世上最難降的妖魔是什么歪今? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮嫉晶,結果婚禮上,老公的妹妹穿的比我還像新娘替废。我一直安慰自己,他們只是感情好舶担,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布彬呻。 她就那樣靜靜地躺著衣陶,像睡著了一般闸氮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蒲跨,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音或悲,去河邊找鬼堪唐。 笑死翎蹈,一個胖子當著我的面吹牛,可吹牛的內容都是我干的荤堪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼澄阳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了低剔?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤户侥,失蹤者是張志新(化名)和其女友劉穎峦嗤,沒想到半個月后蕊唐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡烁设,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年替梨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片装黑。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡副瀑,死狀恐怖,靈堂內的尸體忽然破棺而出恋谭,到底是詐尸還是另有隱情糠睡,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布疚颊,位于F島的核電站狈孔,受9級特大地震影響材义,放射性物質發(fā)生泄漏均抽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一其掂、第九天 我趴在偏房一處隱蔽的房頂上張望油挥。 院中可真熱鬧,春花似錦、人聲如沸深寥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惋鹅。三九已至持灰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間负饲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工喂链, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留返十,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓椭微,卻偏偏與公主長得像洞坑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蝇率,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內容