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

来源:互联网 发布:淘宝网上传图片尺寸 编辑:程序博客网 时间:2024/06/14 10:09

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

/* * 抽象模板类,抽象汽车模型 */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()方法,使宝马能够启动@Overrideprotected void start() {System.out.println("宝马启动!");}//实现汽车抽象模板类stop()方法,使宝马能够刹车@Overrideprotected void stop() {System.out.println("宝马停车!");}//宝马汽车喇叭响声就是“滴滴滴”,继承父类alarm()方法即可//所有汽车的行驶流程都是一致的,继承父类run()方法}
/* *具体实现类,奔驰汽车模型  */public class BENZModel extends AbstractCarModel{//实现汽车抽象模板类的start()方法,使奔驰能够启动@Overrideprotected void start() {System.out.println("奔驰启动!");}//实现汽车抽象模板类stop()方法,使奔驰能够刹车@Overrideprotected void stop() {System.out.println("奔驰停车!");}//奔驰汽车喇叭响声就是“巴拉巴拉巴拉”,需要重写父类alarm()方法@Overrideprotected 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();}}


执行结果:



由上述代码我们可以总结出: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方法就可以了。


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

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

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

1 0