模板方法(Template Method)
[TOC]
如果您对Android开发感兴趣,或者也是正在学习的路人,可以加群一起交流~ 群号:929891705
定义
惯例上白话,以我自己为例,原来做个一个BLE相关的需求,具体需求是这样的,需要对外给客户提供一个和产品连接的接口,说白了就是一个connect的接口,那么有连接必然有断开连接,发送数据,回调接收数据等等。然而真正的连接其实还需要和设备进行通信,握手等一系列骚操作,发送数据也是一样的,需要等设备回复收到的响应才行,同时要对数据做一层加密处理,然而这些外部都不需要考虑,外部只需要考虑传递数据的明文即可。就酱,就是所谓的模版方法模式,尼玛,一脸懵13...
Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.(定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。)
抽象模版的方法分为两类
- 基本方法:基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用。
- 模版方法:可以有一个或几个,一般是一个具体方法,也就是一个框架,实现对基本方法的调度,完成固定的逻辑。
抽象模板中的基本方法尽量设计为protected类型,符合迪米特法则,不需要暴露的属性或方法尽量不要设置为protected类型。实现类若非必要,尽量不要扩大父类中的访问权限。
OK~ 下面来介绍一下模版方法的写法思路
- 定义一个抽象类,抽象方法为子类根据自己特性需要实现的方法
- 定义一个公开的final方法,作为提供上层调用的接口
就酱,尼玛简单的一13。。。(我们好像在哪儿见过.jpg)
code
惯例上背景,本次选择LOL的召唤师作为我们的模版,下面建立召唤师抽象类
private abstract static class AbsPlayer {
private boolean isAction = false;
/**
* 走位
*/
protected abstract void run();
/**
* 攻击
*/
protected abstract void attack();
/**
* 送头
*/
protected abstract void songTou();
/**
* 演员
*
* @param isAct 是否开演
*/
protected abstract void setAction(boolean isAct);
protected final boolean getIsAction() {
return isAction;
}
/**
* 开团
*/
protected final void teamBattle() {
run();
attack();
if (isAction) {
songTou();
} else {
System.out.println("yes we are win!");
}
}
}
一共就三个带有子类特性的接口,分别是走位,攻击和演员开关哈~,接下来上实现子类,分为AD和AP两种召唤师
private static class ApPlayer extends AbsPlayer {
@Override
public void run() {
System.out.println("i am AP-Carry, look me crazy running");
}
@Override
public void attack() {
System.out.println("look me crazy release skills");
}
@Override
public void songTou() {
System.out.println("Oh, i will die, help me, go on daye...oh, i am die...");
}
@Override
public void setAction(boolean isAct) {
System.out.println("i am AP-Carry, i am acting, we must lose!");
super.isAction = isAct;
}
}
private static class AdPlayer extends AbsPlayer {
@Override
public void run() {
System.out.println("i am AD-Carry, look me crazy running");
}
@Override
public void attack() {
System.out.println("look me crazy Aing enemy");
}
@Override
public void songTou() {
System.out.println("Oh, i will die, help me, go on fuzhu...oh, i am die...");
}
@Override
public void setAction(boolean isAct) {
System.out.println("i am AD-Carry, i am acting, we must lose!");
super.isAction = isAct;
}
}
建立完子类,就是上层调用流程了,非常简单
public static void main(String[] args) {
AbsPlayer adc = new AdPlayer();
AbsPlayer apc = new ApPlayer();
apc.setAction(true);
adc.teamBattle();
apc.teamBattle();
}
//log
//i am AP-Carry, i am acting, we must lose!
//i am AD-Carry, look me crazy running
//look me crazy Aing enemy
//yes we are win!
//i am AP-Carry, look me crazy running
//look me crazy release skills
//Oh, i will die, help me, go on daye...oh, i am die...
特点
codeing结束了,下面我们总结一下子,模版都有什么牛哔的地方
优点:
- 封装不变部分,扩展可变部分:把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继续扩展。
- 提取公共部分代码,便于维护:这个很好理解,一只成熟的程序猿,起码也会对自己的代码做最基本的抽取。
- 行为由父类控制,子类实现:基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能,符合开闭原则。这个基本方法也是咱们案例里面的团战方法哈~
缺点:
- 缺点惯例直接贴过来,不过在下认为这不算是缺点,熟悉了模版后很灵活,初看可能会带来一点不适应。按照我们的设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类完成具体的事物属性和方法。但是模板方法模式却颠倒了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响,这在复杂的项目中,会带来代码阅读的难度,而且也会让新手产生不适感。
使用场景
下面同样把书里的贴过来,不过大家不要套,不要硬套,而是要真正理解之后再使用
- 多个子类有公有的方法,并且逻辑基本相同时。
- 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
- 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数(见“模板方法模式的扩展”)约束其行为。