备忘录模式

定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该状态之外保存这个状态。
这样以后就可以将该状态恢复到原来保存的状态。

备忘录模式通用类图

实现

Originator

发起人角色,记录当前时刻的内部状态,负责定义哪些属性备份范围的状态,负责创建和恢复备忘录数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Boy {

private String state = "";

public void change(){
this.state = " I Am So Sad.";
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

/**
* 可以创建多个备份
* @return
*/
public Memento createMemento(){
return new Memento(this.state);
}

/**
* 可以选择任意的备份进行恢复
* @param memento
*/
public void restore(Memento memento){
this.setState(memento.getState());
}
}

Memento

备忘录角色,负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Memento {

private String state = "";

public Memento(String state) {
this.state = state;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}
}

Caretaker

备忘录管理员角色,对备忘录进行管理、保存和提供备忘录。

1
2
3
4
5
6
7
8
9
10
11
12
public class Caretaker {

private Memento memento;

public Memento getMemento() {
return memento;
}

public void setMemento(Memento memento) {
this.memento = memento;
}
}

Use

1
2
3
4
5
6
7
8
9
Caretaker caretaker = new Caretaker();
caretaker.setMemento(boy.createMemento());
System.out.println("Now Boy Is : " + boy.getState());

boy.change();
System.out.println("Now Boy Is : " + boy.getState());

boy.restore(caretaker.getMemento());
System.out.println("Now Boy Is : " + boy.getState());

使用场景

  • 需要保存和恢复数据的相关状态场景
  • 提供一个可回滚的操作
  • 需要监控的副本场景中
  • 数据库连接的事务管理

注意事项

  • 备忘录的生命周期
    主动管理备忘录的生命周期,不实用就需要立即删除其引用。
  • 备忘录的性能
    要控制备忘录的历史记录数量,以及大对象的备忘录。

扩展

Clone方式的备忘录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Originator implements Cloneable {

private String state = "";

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public Originator createMemento(){
return this.clone();
}

public void restoreMemento(Originator originator){
this.setState(originator.getState());
}

@Override
protected Originator clone() {
try {
return (Originator)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class Caretaker {

private Originator originator;

public Originator getOriginator() {
return originator;
}

public void setOriginator(Originator originator) {
this.originator = originator;
}
}

多状态的备忘录模式

把整个对象转换成Map,当然也可以序列化、或者直接复制整个对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Memento {

private HashMap<String,Object> stateMap;

public Memento(HashMap<String, Object> stateMap) {
this.stateMap = stateMap;
}

public HashMap<String, Object> getStateMap() {
return stateMap;
}

public void setStateMap(HashMap<String, Object> stateMap) {
this.stateMap = stateMap;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;

public class BeanUtils {

public static HashMap<String,Object> backupProp(Object bean){
HashMap<String,Object> result = new HashMap<>();

try{
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor descriptor : descriptors) {
String fieldName = descriptor.getName();
Method getter = descriptor.getReadMethod();
Object fieldValue = getter.invoke(bean, new Object[]{});
if (!fieldName.equalsIgnoreCase("class")){
result.put(fieldName, fieldValue);
}
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}


public static void restoreProp(Object bean, HashMap<String,Object> propMap){
try{
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor descriptor : descriptors) {
String fieldName = descriptor.getName();
if (propMap.containsKey(fieldName)){
Method setter = descriptor.getWriteMethod();
setter.invoke(bean, new Object[]{propMap.get(fieldName)});
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Originator {

private String state1 = "";
private String state2 = "";
private String state3 = "";

public String getState1() {
return state1;
}

public void setState1(String state1) {
this.state1 = state1;
}

public String getState2() {
return state2;
}

public void setState2(String state2) {
this.state2 = state2;
}

public String getState3() {
return state3;
}

public void setState3(String state3) {
this.state3 = state3;
}


public Memento createMemento(){
return new Memento(BeanUtils.backupProp(this));
}

public void restoreMemento(Memento memento){
BeanUtils.restoreProp(this, memento.getStateMap());
}
}

多备份的备忘录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Caretaker {

Map<String, Memento> map = new LinkedHashMap<>();

public void backup(String version, Memento memento){
map.put(version, memento);
}

public Memento get(String version){
return map.get(version);
}

public Map<String,Memento> getAll(){
return this.map;
}
}

优雅的实现

Bean 对象专注自己的业务,不需要关心备份和恢复业务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Original implements Cloneable {

private String state;

private String info;

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public String getInfo() {
return info;
}

public void setInfo(String info) {
this.info = info;
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

备份管理员负责版本的备份与恢复工作,职责明晰。
同时要注意历史版本次数,防止版本过多。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Caretacker {

private int size = 10;

private Map<String, Original> histo = new LinkedHashMap<>();

public boolean backup(String key, Original o) {
if (histo.size() >= 10) {
return false;
}
try{
histo.put(key,(Original) o.clone() );
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}

public Original get(String k) {
return histo.get(k);
}
}

命令模式

命令模式的封装性非常好,把请求方(Invoker)和执行方(Receiver)分开,扩展性也非常好。

定义

将一个请求封装成一个对象,从而使得不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

命令模式通用类图

实现

Receiver

抽象接收者,定义每个接收者都必须完成的业务。

1
2
3
public abstract class Receiver{
public abstract void doSomething();
}

具体的Receiver

1
2
3
4
5
6
7
public class Concrete1Reveiver extends Receiver{
public void doSomething(){}
}

public class Concrete2Reveiver extends Receiver{
public void doSomething(){}
}

Command

不同的命令实现都需要继承该类,实现自己的execute逻辑。

1
2
3
public abstract class Command{
public abstract void execute();
}

具体的命令实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ConcreteCommand1 extends Command{
private Receiver receiver;
public ConcreteCommand1(Receiver receiver){
this.receiver = receiver;
}
public void execute(){
this.receiver.doSomething();
}
}

public class ConcreteCommand2 extends Command{
private Receiver receiver;
public ConcreteCommand2(Receiver receiver){
this.receiver = receiver;
}
public void execute(){
this.receiver.doSomething();
}
}

Invoker

1
2
3
4
5
6
7
8
9
public class Invoker{
private Command command;
public void setCommand(Command command){
this.command = command;
}
public void action(){
this.command.execute();
}
}

Use

1
2
3
Invoker invoker = new Invoker();
invoker.setCommand(new ConcreteCommand1(new Concrete1Reveiver()));
invoker.action();

模式应用

优点

  • 解耦
    调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需要调用Command抽象类的execute方法即可,不需要了解是哪个接收者执行。
  • 可扩展性好
    Command子类可以非常容易的扩展,调用者Invoker和高层的模块Client不产生严重的代码耦合。
  • 与其他模式混合使用
    与责任链模式结合,实现命令族解析任务
    与模版方法结合,减少Command子类膨胀的问题。

缺点

Command的子类会随着命令的增多逐步膨胀。

命令的撤销

反命令

通过相反的命令来实现命令的撤销。

结合备忘录模式

结合备忘录模式还原最后状态,折中发昂发适合接收者为状态的变更情况,而不适合事件处理。

代码实例

假设有个录音机,录音机面板有三个按钮, play, rewind, stop。

需要根据三个按钮作出相应的反馈。

Receiver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AudioPlayer {

public void paly(){
System.out.println("Play");
}

public void rewind(){
System.out.println("Rewind");
}

public void stop(){
System.out.println("Stop");
}
}

Command

不同的Command调用不同的Receiver命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public abstract class Command {

protected static AudioPlayer audioPlayer = new AudioPlayer();

public abstract void execute();

}

public class PlayCommand extends Command {
@Override
public void execute() {
super.audioPlayer.paly();
}
}

public class RewindCommand extends Command {
@Override
public void execute() {
audioPlayer.rewind();
}
}

public class StopCommand extends Command {
@Override
public void execute() {
audioPlayer.stop();
}
}

Invoker

用于转发Command

1
2
3
4
5
6
7
8
9
10
11
public class KeyPad {
private Command command;

public void setCommand(Command command) {
this.command = command;
}

public void action() {
this.command.execute();
}
}

Use

1
2
3
4
5
6
7
KeyPad keyPad = new KeyPad();

keyPad.setCommand(new RewindCommand());
keyPad.action();

keyPad.setCommand(new StopCommand());
keyPad.action();

如果后期需要增加一个按钮,stopAndRewind(),直接增加一个Command子类即可,其他地方不需要动。

1
2
3
4
5
6
7
8
public class StopAndRewindCommand extends  Command {

@Override
public void execute() {
audioPlayer.stop();
audioPlayer.rewind();
}
}

责任链模式

责任链模式的核心在链上, 链是有多个处理者组成的。就像流水线上的质检员。

定义

使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的关系。
将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

责任链模式通用类图

实现

抽象事件

抽象事件类型表明这类型的事件是需要被处理的。

1
2
3
public interface IEvent {
int getType();
}

具体的事件

不同类型的子类事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class EventClick implements IEvent {
public int getType() {
return 1;
}
}
public class EventEnter implements IEvent {
public int getType() {
return 2;
}
}
public class EventBlank implements IEvent {
public int getType() {
return 3;
}
}

抽象的事件处理器

定义了只有Ievent类型的事件会被处理。

1
2
3
public interface IEventHandler {
boolean check(IEvent event);
}

具体的事件处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class ClickEventHandler implements IEventHandler {

public boolean check(IEvent event) {
if (event.getType() != 1){
return false;
}
System.out.println("Father check Ok. ");
return true;
}
}

public class EnterEventHandler implements IEventHandler {

public boolean check(IEvent event) {
if (event.getType() == 2) {
System.out.println("Husband Ok.");
return true;
}
return false;
}
}

public class BlankEventHandler implements IEventHandler {

public boolean check(IEvent event) {
if (event.getType() != 3) {
return false;
}

System.out.println("Son Ok");
return true;

}
}

事件处理链

用于组织事件处理器,符合条件的处理器处理完成后或者没有处理器处理都会停止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class EventHandlerChain {
private List<IEventHandler> eventChains = new ArrayList<IEventHandler>();
public void add(IEventHandler eventHandler) {
eventChains.add(eventHandler);
}
public boolean check(IEvent event) {
for (IEventHandler eventChain : this.eventChains) {
if (eventChain.check(event)) {
return true;
}
}
return false;
}

}

Use

可以随意组织责任链的顺序,然后检查需要检查的类型。

1
2
3
4
5
6
7
8
EventHandlerChain chain = new EventHandlerChain();
chain.add(new ClickEventHandler());
chain.add(new EnterEventHandler());
chain.add(new BlankEventHandler());

chain.check(new EventClick());
chain.check(new EventEnter());
chain.check(new EventBlank());

应用

优点

将请求和处理分开。请求着可以不用知道是谁处理的,处理者可以不用知道请求着的全貌。
两者解耦,提高了系统的灵活性。

缺点

性能问题, 最差的情况下,请求从头遍历到尾,链条特别长的时候,性能损耗是个大的问题。
因此在使用的过程中要注意责任链的长短。
调试不方便。

在广告系统中,由不同的事件来触发不同类型的广告时根据责任链模式来处理。
每个Handler可以专注自己的业务处理逻辑。具体Handler还可以抽象一个抽象类,把公用的逻辑写在抽象类里面,每个具体的实现类只负责业务的处理即可(优化为模版方法模式)。