模板方法模式(Template)
- 角色:
- AbstractClass 抽象类实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法ConcreteClass
- 实现抽象方法 , 以完成算法中特点子类的步骤
- 使用前
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();
}
}
使用后
public 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方法运行的时长======当前方法执行时长:40
// new ConcreteClassA().template();
//ConcreteClassB.method方法运行的时长======当前方法执行时长:272
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)
-
术语
-
Command:命令
-
ConcreteCommand:具体的命令
-
Invoker:调用者
-
Receiver:接受者
-
- 角色
-
Command抽象命令 执行命令 撤销命令
-
ConcreteCommandLightOnCommand 开灯 LightOffCommand 关灯
NonCommand
空命令
-
Invoker调用者 遥控器聚合所有命令 Command[] ons Command[] offs Command undo
-
Receiver接受者 电灯、空调、电视
-
出现前
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("电视关闭..."); } } public class Invoker { private Light light = new Light(); private AirConditioner airConditioner = new AirConditioner(); private Television television = new Television(); public void lightOn(){ light.on(); } public void lightOff(){ light.off(); } public void airOn(){ airConditioner.on(); } public void airOff(){ airConditioner.off(); } public void tvOn(){ television.on(); } public void tvOff(){ television.off(); } } public class Client { public static void main(String[] args) { Invoker invoker = new Invoker(); invoker.lightOn(); invoker.lightOff(); System.out.println("============="); invoker.airOn(); invoker.airOff(); System.out.println("============="); invoker.tvOn(); invoker.tvOff(); } }
出现后
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("电视关闭..."); } } package com.javaxl.design.command.after; 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(); } }
-
-
注意事项和细节:
-
将发起请求的对象与执行请求的对象解耦
-
容易实现对请求的撤销和重做
-
空命令也是一种设计模式,它为我们省去了判空的操作
-
命令模式不足: 可能导致某些系统有过多的具体命令类,增加了系统的复杂度
-
与外观模式相似:都是将多个功能聚合在一起 外观模式更多适用于维护;命令模式更多应用于设计;
-
-
备忘录模式(Memento)
-
角色
-
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 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 Caretaker {
private HeroMemento heroMemento;
public HeroMemento getHeroMemento() {
return heroMemento;
}
public void setHeroMemento(HeroMemento heroMemento) {
this.heroMemento = heroMemento;
}
}
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;
}
}
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());
}
}
-
情况3:为多个对象保留一个状态
-
public class Caretaker { private HashMap<Originator ,Memento> mementos = new HashMap(); }
-
-
情况4:为多个对象保留多个状态
-
public class Caretaker { private HashMap<Originator , List<Memento>> mementos = new HashMap(); }
-
-
注意事项和细节
-
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
-
实现了信息的封装,使得用户不需要关心状态的保存细节
-
注意:如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
-