行为型模式
术语
template:模板
角色
AbstractClass 抽象类实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法ConcreteClass 2)实现抽象方法 , 以完成算法中特点子类的步骤
案例
需求:统计某一代码运行时间
使用前:
-
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 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 关键字, 防止子类重写模板方法
-
-
应用
Spring IOC容器加载
命令模式(Command)
-
术语
-
Command:命令
-
ConcreteCommand:具体的命令
-
Invoker:调用者
-
Receiver:接受者
-
标准UML类图
-
角色
-
Command抽象命令 执行命令 撤销命令
-
ConcreteCommandLightOnCommand 开灯 LightOffCommand 关灯
NonCommand
空命令
-
Invoker调用者 遥控器聚合所有命令 Command[] ons Command[] offs Command undo
-
Receiver接受者 电灯、空调、电视
-
-
案例
需求:万能遥控器的制作
案例UML类图
注意事项和细节:
将发起请求的对象与执行请求的对象解耦 容易实现对请求的撤销和重做 空命令也是一种设计模式,它为我们省去了判空的操作
命令模式不足: 可能导致某些系统有过多的具体命令类,增加了系统的复杂度
与外观模式相似:都是将多个功能聚合在一起 外观模式更多适用于维护;命令模式更多应用于设计;
应用:
Spring框架中的JdbcTemplate类 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令 界面的一个按钮都是一条命令、模拟 CMD(DOS 命令)订单的撤销/恢复、触发- 反馈机制
备忘录模式(Memento)
术语:
Memento:备忘录
originator:发起者
Caretaker:守护者
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();
}
}
package com.zking.memento.demo1;
public class Caretaker {
private HeroMemento heroMemento;
public HeroMemento getHeroMemento() {
return heroMemento;
}
public void setHeroMemento(HeroMemento heroMemento) {
this.heroMemento = heroMemento;
}
}
package com.zking.memento.demo1;
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;
}
}
package com.zking.memento.demo1;
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:为一个对象保留多个状态
package com.zking.memento.demo2;
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();
}
}
package com.zking.memento.demo2;
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;
}
}
package com.zking.memento.demo2;
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);
}
}
package com.zking.memento.demo2;
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());
}
}
情况3:为多个对象保留一个状态
public class Caretaker { private HashMap<Originator ,Memento> mementos = new HashMap();}
情况4:为多个对象保留多个对象
public class Caretaker { private HashMap<Originator , List<Memento>> mementos = new HashMap();}
注意事项和细节
-
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
-
实现了信息的封装,使得用户不需要关心状态的保存细节
注意:如果类的成员变量过多,势必会占用比应用事务回滚 游戏的存档 Selenium动态解析资源树 较大的资源,而且每一次保存都会消耗一定的内存
应用
事务回滚
游戏的存档
Selenium动态解析资源树