模板方法模式(Template)
角色
AbstractClass 抽象类实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法
ConcreteClass 实现抽象方法 , 以完成算法中特点子类的步骤
模板模式UML类图
- 使用前
public class CodeTotalTime {
public static void template(){
long start = System.currentTimeMillis();
// 检测Operation_1方法运行的时长======33
Operation_1();
// 检测Operation_2方法运行的时长======616
// Operation_2();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
public static void Operation_1(){
for (int i = 0; i<1000 ;i++){
System.out.println("模拟耗时操作...");
}
System.out.print("检测Operation_1方法运行的时长======");
}
public static void Operation_2(){
for (int i = 0; i<20000 ;i++){
System.out.println("模拟耗时操作...");
}
System.out.print("检测Operation_2方法运行的时长======");
}
}
public class Client {
public static void main(String[] args) {
CodeTotalTime.template();
}
}
- 使用后
abstract class CodeAbstractClass {
public void template() {
long start = System.currentTimeMillis();
method();
long end = System.currentTimeMillis();
System.out.println("当前方法执行时长:" + (end - start));
}
public abstract void method();
}
class ConcreteClassA extends CodeAbstractClass {
@Override
public void method() {
for (int i = 0; i < 1000; i++) {
System.out.println("模拟耗时操作...");
}
System.out.print("检测ConcreteClassA.method方法运行的时长======");
}
}
class ConcreteClassA extends CodeAbstractClass {
@Override
public void method() {
for (int i = 0; i < 1000; i++) {
System.out.println("模拟耗时操作...");
}
System.out.print("检测ConcreteClassA.method方法运行的时长======");
}
}
class ConcreteClassB extends CodeAbstractClass {
@Override
public void method() {
for (int i = 0; i < 20000; i++) {
System.out.println("模拟耗时操作...");
}
System.out.print("ConcreteClassB.method方法运行的时长======");
}
}
public class Client {
public static void main(String[] args) {
//检测ConcreteClassA.method方法运行的时长======当前方法执行时长:
new ConcreteClassA().template();
//ConcreteClassB.method方法运行的时长======当前方法执行时长:
new ConcreteClassB().template();
}
}
钩子函数应用场景:
public abstract class CodeAbstractClass {
public void template() {
long start = System.currentTimeMillis();
if (callback()) method();
long end = System.currentTimeMillis();
System.out.println("当前方法执行时长:" + (end - start));
}
public abstract void method();
public boolean callback() {
return true;
}
}
从上面可以看出:template方法默认是用作统计method方法的执行时长,但是有的时候我们无需统计代码时长,template函数中有一些其它逻辑要执行,在这里我们可以考虑采用钩子函数;钩子函数被子类覆写,覆写成false,那么method方法就不会被调用,不再统计代码时长了;前端框架Vue的生命周期就有多处用到钩子函数;
注意事项和细节
钩子函数在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”
算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
一般模板方法都加上 final 关键字, 防止子类重写模板方法
命令模式(Command)
标准UML类图
- 角色
- Command
抽象命令
执行命令
撤销命令
- ConcreteCommand
LightOnCommand
开灯
LightOffCommand
关灯
NonCommand
空命令
- Invoker
调用者
遥控器聚合所有命令
Command[] ons
Command[] offs
Command undo
- Receiver
接受者
电灯、空调、电视
案例UML类图
注意事项和细节
将发起请求的对象与执行请求的对象解耦
容易实现对请求的撤销和重做
空命令也是一种设计模式,它为我们省去了判空的操作
命令模式不足: 可能导致某些系统有过多的具体命令类,增加了系统的复杂度
与外观模式相似:都是将多个功能聚合在一起 外观模式更多适用于维护;命令模式更多应用于设计;
public class Light {
public void on() {
System.out.println("电灯打开...");
}
public void off() {
System.out.println("电灯关闭...");
}
}
class AirConditioner {
public void on() {
System.out.println("空调打开...");
}
public void off() {
System.out.println("空调关闭...");
}
}
class Television {
public void on() {
System.out.println("电视打开...");
}
public void off() {
System.out.println("电视关闭...");
}
}
interface Command {
void execute();
void undo();
}// 空命令
class NonCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
class LightOnCommand implements Command {
private Light light = new Light();
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
class LightOffCommand implements Command {
private Light light = new Light();
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
class TvOnCommand implements Command {
private Television tv = new Television();
@Override
public void execute() {
tv.on();
}
@Override
public void undo() {
tv.off();
}
}
class TvOffCommand implements Command {
private Television tv = new Television();
@Override
public void execute() {
tv.off();
}
@Override
public void undo() {
tv.on();
}
}
public class Invoker {
Command[] ons;
Command[] offs;// 记录上一个命令
Command command;
public Invoker(int n) {
ons = new Command[n];
offs = new Command[n];
command = new NonCommand();
for (int i = 0; i < n; i++) {
setCommand(i, new NonCommand(), new NonCommand());
}
}
public void setCommand(int no, Command on, Command off) {
ons[no] = on;
offs[no] = off;
}
public Command getOnCommand(int no) {
return ons[no];
}
public Command getOffCommand(int no) {
return offs[no];
}
// 执行命令
public void invoke(Command command) {
// 执行当前命令
command.execute();// 保存当前执行命令
this.command = command;
}// 撤销命令(上个操作的反操作)
public void undo() {// 这里就能体现定义一个空命令的好处了,如果第一次按撤销命令,那么应该什么都不做;// 如果没有定义空命令的话,此时就需要判断空处理了
command.undo();
}
}
public class Client {
public static void main(String[] args) {
Invoker invoker = new Invoker(2);
invoker.setCommand(0, new LightOnCommand(), new LightOffCommand());
invoker.setCommand(1, new TvOnCommand(), new TvOffCommand());
System.out.println("电灯打开关闭操作===========");
invoker.invoke(invoker.getOnCommand(0));
invoker.invoke(invoker.getOffCommand(0));
// invoker.undo();
System.out.println("电视打开关闭操作===========");
invoker.invoke(invoker.getOnCommand(1));
invoker.undo();
}
}
- 应用
Spring框架中的JdbcTemplate类
容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
界面的一个按钮都是一条命令、模拟 CMD(DOS 命令)订单的撤销/恢复、触发- 反馈机制
备忘录模式(Memento)
UML类图
角色
originator 待保存状态的对象 ~ Hero
Memento 备忘录对象
Caretaker
存放备忘录对象的容器;可以是List、Map、或者单个Memento对象 可以保存多个 originator 对象的不同时间的状态
-
案例
需求:游戏人物大战后状态恢复
-
情况1:为一个对象保留一个状态
public class Hero {
// 需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
private String state;
public Hero(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
} // 将当前Hero对象实例进行备份
public HeroMemento saveHero() {
return new HeroMemento(this.state);
}
// 恢复上一个英雄状态
public void getMemento(HeroMemento heroMemento) {
this.state = heroMemento.getState();
}
}
public class Caretaker {
private HeroMemento heroMemento;
public HeroMemento getHeroMemento() {
return heroMemento;
}
public void setHeroMemento(HeroMemento heroMemento) {
this.heroMemento = heroMemento;
}
}
public class HeroMemento {
private String state;
public HeroMemento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
public class Client {
public static void main(String[] args) {
Hero hero = new Hero("状态1,满血状态");
Caretaker caretaker = new Caretaker();
caretaker.setHeroMemento(hero.saveHero());
hero.setState("状态2:状态下滑");
System.out.println("当前的状态===============" + hero.getState());
hero.getMemento(caretaker.getHeroMemento());
System.out.println("当前的状态===============" + hero.getState());
caretaker.setHeroMemento(hero.saveHero());
hero.setState("状态3:残血状态");
hero.getMemento(caretaker.getHeroMemento());
System.out.println("当前的状态===============" + hero.getState());
caretaker.setHeroMemento(hero.saveHero());
hero.setState("状态4:临死状态");
caretaker.setHeroMemento(hero.saveHero());
}
}
执行结果为:
情况2:为一个对象保留多个状态
public class Hero {
// 需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
private String state;
public Hero(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
} // 将当前Hero对象实例进行备份
public HeroMemento saveHero() {
return new HeroMemento(this.state);
} // 恢复某一个英雄状态
public void getMemento(Caretaker caretaker, int no) {
this.state = caretaker.getMemento(no).getState();
}
}
public class HeroMemento {
private String state;
public HeroMemento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
import java.util.ArrayList;
import java.util.List;
public class Caretaker {
private List<HeroMemento> heroMementos = new ArrayList<>();
public void addMemento(HeroMemento memento) {
heroMementos.add(memento);
}
public HeroMemento getMemento(int no) {
return heroMementos.get(no);
}
}
public class Client {
public static void main(String[] args) {
Hero hero = new Hero("状态1,满血状态");
Caretaker caretaker = new Caretaker();
caretaker.addMemento(hero.saveHero());
hero.setState("状态2:状态下滑");
hero.setState("状态3:残血状态");
caretaker.addMemento(hero.saveHero());
hero.setState("状态4:临死状态");
caretaker.addMemento(hero.saveHero());
hero.setState("状态5:死亡状态");
// 上面备份了1、3、4状态,我来恢复看看
System.out.println("当前的状态===============" + hero.getState());
hero.getMemento(caretaker, 0);
System.out.println("回复到状态1===============" + hero.getState());
hero.getMemento(caretaker, 1);
System.out.println("回复到状态3===============" + hero.getState());
hero.getMemento(caretaker, 2);
System.out.println("回复到状态4===============" + hero.getState());
}
}
注意事项和细节
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
实现了信息的封装,使得用户不需要关心状态的保存细节
注意:如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
- 应用
事务回滚
游戏的存档 Selenium动态解析资源树
至此,行为型模式介绍完毕,由于作者水平有限难免有疏漏,欢迎留言纠错。