从模板方法模式深入理解Java抽象类

二话不说先上代码,如下所示为一个抽象类(抽象汽车模型)与它的两个具体实现类(宝马模型、奔驰模型)的模拟程序:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * 抽象模板类,抽象汽车模型 
  3.  */  
  4. public abstract class AbstractCarModel {  
  5.     //汽车要能启动!<span style="white-space:pre">       </span>  
  6.     protected abstract void start();<span style="white-space:pre">  
  7. </span>   //汽车要能刹车!  
  8.     protected abstract void stop();  
  9.     //汽车要能响喇叭!  
  10.     protected void alarm(){  
  11.         //一般汽车喇叭都是“滴滴滴”哦,要想“巴拉拉”或者“啪啪啪”去实现类自己重写去!  
  12.         System.out.println("滴滴滴滴滴滴");  
  13.     }  
  14.     //最重要的是,汽车要能行驶!而且行驶流程是固定的,不能改变!  
  15.     protected final void run(){  
  16.         //启动  
  17.         this.start();  
  18.         //鸣笛  
  19.         this.alarm();  
  20.         //到达目的地就停车  
  21.         this.stop();  
  22.     }  
  23. }  

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * 具体实现类,宝马汽车模型 
  3.  */  
  4. public class BMWModel extends AbstractCarModel{  
  5.   
  6.     //实现汽车抽象模板类的start()方法,使宝马能够启动  
  7.     @Override  
  8.     protected void start() {  
  9.         System.out.println("宝马启动!");  
  10.     }  
  11.     //实现汽车抽象模板类stop()方法,使宝马能够刹车  
  12.     @Override  
  13.     protected void stop() {  
  14.         System.out.println("宝马停车!");  
  15.     }  
  16.     //宝马汽车喇叭响声就是“滴滴滴”,继承父类alarm()方法即可  
  17.     //所有汽车的行驶流程都是一致的,继承父类run()方法  
  18. }  
[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  *具体实现类,奔驰汽车模型  
  3.  */  
  4. public class BENZModel extends AbstractCarModel{  
  5.     //实现汽车抽象模板类的start()方法,使奔驰能够启动  
  6.         @Override  
  7.         protected void start() {  
  8.             System.out.println("奔驰启动!");  
  9.         }  
  10.         //实现汽车抽象模板类stop()方法,使奔驰能够刹车  
  11.         @Override  
  12.         protected void stop() {  
  13.             System.out.println("奔驰停车!");  
  14.         }  
  15.         //奔驰汽车喇叭响声就是“巴拉巴拉巴拉”,需要重写父类alarm()方法  
  16.         @Override  
  17.         protected void alarm(){  
  18.             System.out.println("巴拉巴拉巴拉");  
  19.         }  
  20.         //所有汽车的行驶流程都是一致的,继承父类run()方法  
  21. }  

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * 实现场景类 
  3.  */  
  4. public class Client {  
  5.     public static void main(String[] args){  
  6.           
  7.         //做个宝马  
  8.         BMWModel bmw = new BMWModel();  
  9.         //开辆奔驰  
  10.         BENZModel benz = new BENZModel();  
  11.         //宝马跑一跑  
  12.         bmw.run();  
  13.         System.out.println("*************分割线*************");  
  14.         //奔驰跑一跑  
  15.         benz.run();  
  16.     }  
  17. }  

 

 

执行结果:


由上述代码我们可以总结出:1、我们说抽象类体现的是一种模板式的设计,它只有被具体实现类继承并实现时才有意义。如上例中,AbstractCarModel类是一个抽象类,它的实例是没有意义的,它的具体实现类的实例才有意义。

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方法就可以了。

 

钩子方法和具体方法的存在使抽象类与接口具有了最主要的差异:

接口主要体现的是一种规范,实现接口的类只能去实现这种规范,但用户通过与统一接口对接实现了规范与实现的分离,极大地降低了模块间的耦合度;

而通过模板方法模式使用抽象类,可以继承某些具体方法,实现了规范并增加了代码的可重用性,而继承抽象方法与钩子方法使实现类可以灵活地扩展抽象模板类,接口并不能有这样的灵活扩展特性,是非常常用而有意义的一种设计模式!

posted @ 2016-12-05 12:11  天涯海角路  阅读(103)  评论(0)    收藏  举报