模板模式

假设我们现在要造车,因为汽车大部分的功能是差不多的,都会有启动引擎、启动汽车、鸣笛、停车等几个方法,所以我们就可以把这些方法提取抽象成公共的方法。

package cn.java.template;

public abstract class Car {
	//以下是要子类具体实现的抽象方法
	protected abstract void start();
	protected abstract void stop();
	protected abstract void alarm();
	protected abstract void engineBoom();
	//模板方法
	protected final void run(){
		this.engineBoom();
		this.start();
		this.alarm();
		this.stop();
	}

}
上面的是汽车的父类,定义了公有的抽象方法,因为汽车启动的顺序步骤都是一致的,所以我们在抽象父类中具体实现了执行顺序,使用了final关键字尽量减少公共逻辑方法的覆写。抽象方法使用了protected限制方法只允许在本类和子类间访问。

package cn.java.template;

public class Bmw extends Car {

	@Override
	protected void start() {
		System.out.println("宝马启动!!!");

	}

	@Override
	protected void stop() {
		// TODO Auto-generated method stub
		System.out.println("宝马停车!!!");
	}

	@Override
	protected void alarm() {
		// TODO Auto-generated method stub
		System.out.println("宝马鸣笛!!!!");
	}

	@Override
	protected void engineBoom() {
		// TODO Auto-generated method stub
		System.out.println("宝马引擎轰轰轰!!!");
	}

}
package cn.java.template;

public class DasAuto extends Car {

	@Override
	protected void start() {
		// TODO Auto-generated method stub
		System.out.println("奥迪车启动!!!");
	}

	@Override
	protected void stop() {
		// TODO Auto-generated method stub
		System.out.println("奥迪停车!!!");
	}

	@Override
	protected void alarm() {
		// TODO Auto-generated method stub
		System.out.println("奥迪鸣笛!!!");
	}

	@Override
	protected void engineBoom() {
		// TODO Auto-generated method stub
		System.out.println("奥迪引擎呜呜呜呜呜呜呜!!!!");
	}

}
以上两个是具体的子类实现。
package cn.java.template;

public class Client {
	public static void main(String[] args) {
		Car q1=new Bmw();
		Car q2=new DasAuto();
		q1.run();
		q2.run();
	}
}
在这个场景类中进行调用。挺简单的。


模板方法模式定义:定义一个操作中的算法的框架,儿将一些步骤延迟到子类中去实现。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

基本方法:基本方法也叫作基本操作,是由子类实现的方法,并且在模板方法中被调用。

模板方法:可以有一个或多个,一般是一个具体方法,也就是一个框架,实现对基本方法的调度,完成固定逻辑。

注意:一般模板方法为了防止恶意调用,都加上final关键字,不允许被覆写。

模板方法模式的扩展

如果此时我们对设计的汽车进行了修改,要让有的车子能鸣笛有的车子不能鸣笛,应该如何设计?

package cn.java.template;

public abstract class Car {
	//以下是要子类具体实现的抽象方法
	protected abstract void start();
	protected abstract void stop();
	protected abstract void alarm();
	protected abstract void engineBoom();
	//钩子方法
	protected  boolean isAlarm(){
		return true;
	}
	
	//模板方法
	protected final void run(){
		this.engineBoom();
		this.start();
		if(isAlarm()){
			this.alarm();
		}
		this.stop();
	}

}
在抽象父类中我们定义了一个isAlarm()方法,让它决定是否要执行alarm方法,默认是执行的。

package cn.java.template;

public class Bmw extends Car {

	@Override
	protected void start() {
		System.out.println("宝马启动!!!");

	}

	@Override
	protected void stop() {
		// TODO Auto-generated method stub
		System.out.println("宝马停车!!!");
	}

	@Override
	protected void alarm() {
		// TODO Auto-generated method stub
		System.out.println("宝马鸣笛!!!!");
	}
	
	@Override
	protected void engineBoom() {
		// TODO Auto-generated method stub
		System.out.println("宝马引擎轰轰轰!!!");
	}

	@Override
	protected boolean isAlarm() {
		// TODO Auto-generated method stub
		return false;
	}

}
我们在具体的子类bmw中覆写了isAlarm(),使它返回false。

package cn.java.template;

public class Client {
	public static void main(String[] args) {
		Car bmw=new Bmw();
		bmw.run();
	}
}
可以看到在控制台输出了


这种模式在开源框架中经常使用,父类建立框架,子类在重写了父类部分的方法后,再调用从父类继承的方法,产生不同的结果,看上去就像在父类中调用子类的方法。

模板方法模式的有点:

1.封装不变部分,扩展可变部分。把固定不变的一些逻辑方法封装到父类中的模板方法中,而可变的部分则交由具体的子类去实现。

2.提取公共部分代码,便于维护。

3.行为由父类控制,子类实现。
使用场景:

1.多个子类有公有的方法,并且逻辑基本相同时

2.重要、复杂的算法,可以把核心的算法设计为模板方法,周边相关细节功能则由各个子类实现。

3.重构时,模板方法模式是经常使用的模式,把相同的代码提取到父类,然后通过钩子函数约束其行为。

在jdk的AbstractQueuedSynchronizer中,多个方法也使用了模板设计模式。

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
线程执行acquire()方法时尝试在独占模式下获取状态变量,tryAcquire()方法中描述了应该如何去具体的获取状态变量,不同的子类有不同的业务逻辑方法,此处的acquire()方法相当于一个模板方法,在其中定义了不同子类公共的处理算法,即如果不能正确的获取状态变量,那么线程则会被acquireQueued阻塞。至于是如何去获取状态变量的则交由具体的子类去实现tryAcquire()。相应的在release()方法中也使用了模板模式。
public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
但是在aqs框架中,tryAcquire()和tryRelease()没有用abstract修饰,因为在aqs中一些公有方法在具体子类中是不用实现的,所以为防止所有的方法都要子类去实现就避免了使用abstract关键字修饰,而是由子类要实现哪几个具体的方法自己决定。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值