共计 5684 个字符,预计需要花费 15 分钟才能阅读完成。
写在后面
- 记录学习设计模式的笔记
- 进步对设计模式的灵活运用
学习地址
https://www.bilibili.com/vide…
https://www.bilibili.com/vide…
参考文章
http://c.biancheng.net/view/1…
** 我的项目源码
https://gitee.com/zhuang-kang/DesignPattern**
15,模板办法模式
15.1 模板办法模式的定义和特点
模板办法(Template Method)模式的定义如下: 定义一个操作中的算法骨架,而将算法的一些步骤提早到子类中,使得子类能够不扭转该算法构造的状况下重定义该算法的某些特定步骤 ,它是一品种行为型模式。
该模式的次要长处如下。
- 它封装了不变局部,扩大可变局部。它把认为是不变局部的算法封装到父类中实现,而把可变局部算法由子类继承实现,便于子类持续扩大。
- 它在父类中提取了公共的局部代码,便于代码复用。
- 局部办法是由子类实现的,因而子类能够通过扩大形式减少相应的性能,合乎开闭准则。
该模式的次要毛病如下。
- 对每个不同的实现都须要定义一个子类,这会导致类的个数减少,零碎更加宏大,设计也更加形象,间接地减少了零碎实现的复杂度。
- 父类中的形象办法由子类实现,子类执行的后果会影响父类的后果,这导致一种反向的控制结构,它进步了代码浏览的难度。
- 因为继承关系本身的毛病,如果父类增加新的形象办法,则所有子类都要改一遍。
15.2 模板办法模式的构造与实现
15.2.1 模板办法模式的构造
模板办法(Template Method)模式蕴含以下次要角色:
-
抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板办法和若干个根本办法形成。
- 模板办法:定义了算法的骨架,按某种顺序调用其蕴含的根本办法。
-
根本办法:是实现算法各个步骤的办法,是模板办法的组成部分。根本办法又能够分为三种:
- 形象办法 (Abstract Method):一个形象办法由抽象类申明、由其具体子类实现。
- 具体方法 (Concrete Method):一个具体方法由一个抽象类或具体类申明并实现,其子类能够进行笼罩也能够间接继承。
-
钩子办法 (Hook Method):在抽象类中曾经实现,包含用于判断的逻辑办法和须要子类重写的空办法两种。
个别钩子办法是用于判断的逻辑办法,这类办法名个别为 isXxx,返回值类型为 boolean 类型。
- 具体子类(Concrete Class):实现抽象类中所定义的形象办法和钩子办法,它们是一个顶级逻辑的组成步骤。
15.2.2 代码实现
AbstractClass
package com.zhuang.template;
/**
* @Classname AbstractClass
* @Description 抽象类
* @Date 2021/3/26 20:06
* @Created by dell
*/
public abstract class AbstractClass {public final void work() {
// 起床
this.wake();
// 刷牙
this.brush();
// 吃早饭
this.breakfast();
// 交通工具
this.transport();
// 睡觉
this.sleep();}
// 步骤一样 间接实现
public void wake() {System.out.println("起床...");
}
// 步骤一样 间接实现
public void brush() {System.out.println("刷牙...");
}
// 步骤不一样 (一个是吃面包 一个是喝牛奶)
public abstract void breakfast();
// 步骤不一样 (一个是开车 一个是坐地铁)
public abstract void transport();
// 步骤一样 间接实现
public void sleep() {System.out.println("睡觉...");
}
}
ConcreteClass_BreakFast
package com.zhuang.template;
/**
* @Classname ConcreteClass_BreakFast
* @Description 具体类 早饭类 继承
* @Date 2021/3/26 20:13
* @Created by dell
*/
public class ConcreteClass_BreakFast extends AbstractClass {
@Override
public void breakfast() {System.out.println("吃面包...");
}
@Override
public void transport() {System.out.println("坐公交...");
}
}
ConcreteClass_Transport
package com.zhuang.template;
/**
* @Classname ConcreteClass_Transport
* @Description 交通工具类 继承
* @Date 2021/3/26 20:14
* @Created by dell
*/
public class ConcreteClass_Transport extends AbstractClass {
@Override
public void breakfast() {System.out.println("喝牛奶...");
}
@Override
public void transport() {System.out.println("乘地铁...");
}
}
Client
package com.zhuang.template;
/**
* @Classname Client
* @Description 模板办法模式 测试类
* @Date 2021/3/26 20:16
* @Created by dell
*/
public class Client {public static void main(String[] args) {
// 吃面包 坐公交
System.out.println("周一");
AbstractClass breakFast = new ConcreteClass_BreakFast();
breakFast.work();
System.out.println("========================");
System.out.println("周五");
AbstractClass transport = new ConcreteClass_Transport();
transport.work();}
}
钩子办法
AbstractClass
package com.zhuang.template.hook_method;
/**
* @Classname AbstractClass
* @Description 抽象类
* @Date 2021/3/26 20:06
* @Created by dell
*/
public abstract class AbstractClass {public final void work() {
// 起床
this.wake();
// 刷牙
this.brush();
// 吃早饭
this.breakfast();
// 交通工具
if (isSunday()) {this.transport();
}
// 睡觉
this.sleep();}
// 步骤一样 间接实现
public void wake() {System.out.println("起床...");
}
// 步骤一样 间接实现
public void brush() {System.out.println("刷牙...");
}
// 步骤不一样 (一个是吃面包 一个是喝牛奶)
public abstract void breakfast();
// 步骤不一样 (一个是开车 一个是坐地铁)
public abstract void transport();
// 步骤一样 间接实现
public void sleep() {System.out.println("睡觉...");
}
// 钩子办法 是否为周末 周末不必交通工具
boolean isSunday() {return false;}
}
ConcreteClass_BreakFast
package com.zhuang.template.hook_method;
/**
* @Classname ConcreteClass_BreakFast
* @Description 具体类 早饭类 继承
* @Date 2021/3/26 20:13
* @Created by dell
*/
public class ConcreteClass_BreakFast extends AbstractClass {
@Override
public void breakfast() {System.out.println("吃面包...");
}
@Override
public void transport() {System.out.println("坐公交...");
}
}
ConcreteClass_Transport
package com.zhuang.template.hook_method;
/**
* @Classname ConcreteClass_Transport
* @Description 交通工具类 继承
* @Date 2021/3/26 20:14
* @Created by dell
*/
public class ConcreteClass_Transport extends AbstractClass {
@Override
public void breakfast() {System.out.println("喝牛奶...");
}
@Override
public void transport() {System.out.println("乘地铁...");
}
}
ConcreteClass_Sunday
package com.zhuang.template.hook_method;
/**
* @Classname ConcreteClass_Sunday
* @Description 周末 不必下班 空实现交通办法
* @Date 2021/3/26 20:28
* @Created by dell
*/
public class ConcreteClass_Sunday extends AbstractClass{
@Override
public void breakfast() {System.out.println("吃面包或者喝牛奶...");
}
@Override
public void transport() {// 空实现}
@Override
boolean isSunday() {System.out.println("明天周末,劳动...");
return true;
}
}
Client
package com.zhuang.template.hook_method;
/**
* @Classname Client
* @Description 模板办法 测试钩子办法
* @Date 2021/3/26 20:26
* @Created by dell
*/
public class Client {public static void main(String[] args) {AbstractClass sunday = new ConcreteClass_Sunday();
sunday.work();}
}
15.3 模板办法模式的利用场景
- 算法的整体步骤很固定,但其中个别局部易变时,这时候能够应用模板办法模式,将容易变的局部形象进去,供子类实现。
- 当多个子类存在公共的行为时,能够将其提取进去并集中到一个公共父类中以防止代码反复。首先,要辨认现有代码中的不同之处,并且将不同之处拆散为新的操作。最初,用一个调用这些新的操作的模板办法来替换这些不同的代码。
- 当须要管制子类的扩大时,模板办法只在特定点调用钩子操作,这样就只容许在这些点进行扩大。
15.4 JDK 源码解析
InputStream 类就应用了模板办法模式。在 InputStream 类中定义了多个 read()
办法,如下:
public abstract class InputStream implements Closeable {
// 形象办法,要求子类必须重写
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException {return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {if (b == null) {throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {throw new IndexOutOfBoundsException();
} else if (len == 0) {return 0;}
int c = read(); // 调用了无参的 read 办法,该办法是每次读取一个字节数据
if (c == -1) {return -1;}
b[off] = (byte)c;
int i = 1;
try {for (; i < len ; i++) {c = read();
if (c == -1) {break;}
b[off + i] = (byte)c;
}
} catch (IOException ee) { }
return i;
}
}
从下面代码能够看到,无参的 read()
办法是形象办法,要求子类必须实现。而 read(byte b[])
办法调用了 read(byte b[], int off, int len)
办法,所以在此处重点看的办法是带三个参数的办法。
在该办法中第 18 行、27 行,能够看到调用了无参的形象的 read()
办法。
总结如下:在 InputStream 父类中曾经定义好了读取一个字节数组数据的办法是每次读取一个字节,并将其存储到数组的第一个索引地位,读取 len 个字节数据。具体如何读取一个字节数据呢?由子类实现 。
写在最初
- 如果我的文章对你有用,请给我点个👍,感激你😊!
- 有问题,欢送在评论区指出!💪