從模板方法模式深入理解Java抽象類
二話不說先上程式碼,如下所示為一個抽象類(抽象汽車模型)與它的兩個具體實現類(寶馬模型、賓士模型)的模擬程式:
/* * 抽象模板類,抽象汽車模型 */ public abstract class AbstractCarModel { //汽車要能啟動!<span style="white-space:pre"> </span> protected abstract void start();<span style="white-space:pre"> </span> //汽車要能剎車! protected abstract void stop(); //汽車要能響喇叭! protected void alarm(){ //一般汽車喇叭都是“滴滴滴”哦,要想“巴拉拉”或者“啪啪啪”去實現類自己重寫去! System.out.println("滴滴滴滴滴滴"); } //最重要的是,汽車要能行駛!而且行駛流程是固定的,不能改變! protected final void run(){ //啟動 this.start(); //鳴笛 this.alarm(); //到達目的地就停車 this.stop(); } }
/* * 具體實現類,寶馬汽車模型 */ public class BMWModel extends AbstractCarModel{ //實現汽車抽象模板類的start()方法,使寶馬能夠啟動 @Override protected void start() { System.out.println("寶馬啟動!"); } //實現汽車抽象模板類stop()方法,使寶馬能夠剎車 @Override protected void stop() { System.out.println("寶馬停車!"); } //寶馬汽車喇叭響聲就是“滴滴滴”,繼承父類alarm()方法即可 //所有汽車的行駛流程都是一致的,繼承父類run()方法 }
/* *具體實現類,賓士汽車模型 */ public class BENZModel extends AbstractCarModel{ //實現汽車抽象模板類的start()方法,使賓士能夠啟動 @Override protected void start() { System.out.println("賓士啟動!"); } //實現汽車抽象模板類stop()方法,使賓士能夠剎車 @Override protected void stop() { System.out.println("賓士停車!"); } //賓士汽車喇叭響聲就是“巴拉巴拉巴拉”,需要重寫父類alarm()方法 @Override protected void alarm(){ System.out.println("巴拉巴拉巴拉"); } //所有汽車的行駛流程都是一致的,繼承父類run()方法 }
/*
* 實現場景類
*/
public class Client {
public static void main(String[] args){
//做個寶馬
BMWModel bmw = new BMWModel();
//開輛賓士
BENZModel benz = new BENZModel();
//寶馬跑一跑
bmw.run();
System.out.println("*************分割線*************");
//賓士跑一跑
benz.run();
}
}
執行結果:
2、我們知道,不合理使用類的繼承會破壞父類封裝性(子類可能篡改父類實現),所以抽象類的正確使用姿勢是:封裝不變部分,擴充套件可變部分。如上例中,run()方法是被AbstractCarModel抽象類用final關鍵字封裝好的,不允許篡改。而start()方法、stop()方法與alarm()方法都是可變的,子類去實現或者重寫,合理地實現了擴充套件。
3、這其實就是一個簡單的模板方法模式的體現,我在阿里巴巴面試的時候被問到:用介面不能實現,非抽象類實現不可的應用場景?我這時候就該回答模板方法設計模式~~~~(>_<)~~~~
下面詳細介紹模板方法模式
模板方法模式的重點在於抽象模板類的方法型別,主要分三種:
1、抽象方法:由抽象類宣告,用abstract關鍵字標識,由具體實現類去實現。如上例所示的start()方法與stop()方法。
2、具體方法:由抽象類宣告並實現,用final關鍵字標識,在具體實現類中只能呼叫。如上例所示的run()方法。
3、鉤子方法:由抽象類宣告並實現,具體實現類可以繼承抽象父類的預設實現,也可以根據具體情況進行修改擴充套件。如上例所示的alarm()方法,BMWModle類就是繼承了AbstractCarModel抽象類alarm()方法的預設實現;而BENZModel類就是重寫了alarm()方法。
HttpServlet中的鉤子方法:
鉤子方法經常是一個空的實現,因為一個實現類並不需要全部的方法,比如HttpServlet類中的doPost()、doGet()、doPut()、doDelete()、doHead()……為處理HTTP請求,每一種HTTP方法對應著一個do方法,這也是鉤子方法預設的命名規則。這些方法在HttpServlet抽象類中都是以空實現的鉤子方法存在的。在具體實現的時候,一般一個Servlet只需要處理某幾個HTTP方法,在具體的servlet中就重寫對應的幾個do方法就可以了。
鉤子方法和具體方法的存在使抽象類與介面具有了最主要的差異:
介面主要體現的是一種規範,實現介面的類只能去實現這種規範,但使用者通過與統一介面對接實現了規範與實現的分離,極大地降低了模組間的耦合度;
而通過模板方法模式使用抽象類,可以繼承某些具體方法,實現了規範並增加了程式碼的可重用性,而繼承抽象方法與鉤子方法使實現類可以靈活地擴充套件抽象模板類,介面並不能有這樣的靈活擴充套件特性,是非常常用而有意義的一種設計模式!