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

/*
 * 抽象模板类,抽象汽车模型
 */
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();
	}
}


执行结果:



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


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

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

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


本文转载:CSDN博客