享元模式

享元模式的目的在于运用共享技术,使得一些细粒度的对象可以共享。
多食用细粒度的对象,以便于重用或者重构。

定义

使用共享对象可以有效地支持大量的细粒度的对象。

大量的细粒度对象,需要把对象的共有属性提取出来,做为内部状态,剩余的非共有做为外部状态。
内部状态:可以共享出来的信息,存储在襄垣对象内部,并且不会随着环境的改变而改变。
外部状态:对象得以依赖的一个标记,是随着环境的改变而改变的,不可以共享的状态。

享元模式通用类图

实现

Flyweight

抽象享元角色,产品抽象类,同时定义出对象的外部状态和内部状态的接口或者实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract  class Flyweight{
private String instrinsic; //inner status
protected final String extrinsic; //external status

public Flyweight(String extrinsic){
this.extrinsic = extrinsic;
}

public abstract void operate();

public String getInstrinsic(){
return this.instrinsic;
}

public void setInstrinsic(String instrinsic){
this.instrinsic = instrinsic;
}
}

ConcreteFlyweight

具体享元角色,实现抽象角色的定义业务,该角色中需要注意的是内部状态处理应该与环境无关。

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

public ConcreteFlyweight1(String _Extrinsic){
super(_Extrinsic);
}

public void operate(){
//...
}
}

public class ConcreteFlyweight2 extends Flyweight{

public ConcreteFlyweight2(String _Extrinsic){
super(_Extrinsic);
}

public void operate(){
//...
}
}

unsharConcreteFlyweight

不可共享的享元角色

不存在外部状态或者安全要求不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。

FlyweightFactory

享元工厂,构造一个池容器,同时提供从池中获得对象的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class FlyweightFactory{
private static HashMap<String, Flyweight> pool = new HashMap<>();
public static Flyweight getFlyweight(String extrinsic){

Flyweight flyweight = null;

if(pool.containsKey(extrinsic)){
flyweight = pool.get(extrinsic);
}else{
flyweight = new ConcreteFlyweight1(extrinsic);
pool.put(extrinsic, flyweight);
}
return flyweight;
}
}

应用

优点

可以减少应用程序创建的对象,降低程序内存的占用,增强程序的性能。

缺点

提高了程序的复杂性,需要分离出内部状态和外部状态,而且外部状态具有固话特征,不能随着内部状态的改变而改变。

扩展

  • 线程安全的问题
    创建的对象尽量多,多到满足业务对象为止。
  • 性能平衡
    外部状态最好以基本类型为标志,如String, Int等可以大幅提升效率。

门面模式

门面模式也叫外观模式。

定义

要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。

外观模式提供一个高层次的接口,使得子系统更易于使用。

门面模式注重要求“统一的对象”,提供一个访问子系统的接口,除了这个接口不允许任何访问子系统的行为发生。

外观模式

实现

Subsystem

子系统角色,可以同时有一个或者多个子系统,每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面知识一个客户端而已。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class classA{
public void doSomethingA(){
//..logic a
}
}

public class classB{
public void doSomethingB(){
//. logic b
}
}

public class classC{
public void doSomethingC(){
//. logic c.
}
}

Facade

门面角色,客户端可以调用这个角色的方法。这个角色知道子系统的所有功能和责任。一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统,也就是说改角色没有实际的业务逻辑,只是一个委托类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Facade{
private classA a = new classA();
private classB b = new classB();
private classC c = new classC();

public void methodA(){
this.a.doSomethingA();
}

public void methodB(){
this.b.doSomethingB();
}

public void methodC(){
this.c.doSomethingC();
}
}

应用

优点

  • 减少系统的相互依赖
  • 提高了灵活性
  • 提高安全性

使用场景

  • 为一个复杂的模块或者子系统提供一个供外界访问的接口
  • 子系统相对独立
  • 预防低水平人员带来的风险扩散
  • 一个子系统可以有多个门面
  • 门面不参与子系统内的业务逻辑

适配器模式

当我们去国外旅行时,我们国内的插头不能直接插入到国外的插线板上,我们需要购买一个电源适配器来适配国外的插线板。

或者最新版的macbook pro 用的是utypec,并不能直接插入usb3.0的u盘,我们需要购买一个适配器做转换。

定义

将一个类的接口编程客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

适配器模式通用类图

实现

Target

目标角色是一个接口或者抽象类,是一个已经存在的角色,不可能去修改角色中的方法。

1
2
3
public interface Target{
public void request();
}

ConcreteTarget

具体的目标角色实现类。

1
2
3
4
5
public class ConcreteTarget implements Target{
public void request(){
//...
}
}

Adaptee

源角色,已经使用中的角色。需要将Adaptee转换为Target。

1
2
3
4
5
public class Adaptee{
public void doSomething(){
//...
}
}

Adaptor

适配器角色。

1
2
3
4
5
public class Adaptor extends Adaptee implements Target{
public void request(){
super.doSomething();
}
}

Use

1
2
3
4
5
6

Target target = new ConcreteTarget();
target.request();

Target target2 = new Adaptor();
target2.request();

扩展

当需要把多个Adaptee转换为Target的时候,就不能使用继承了,因为Java中不支持多继承。
这种方式叫做对象适配器。
我们可以使用聚合(关联)来完成适配。

对象适配器类图

类适配器是由类的继承关系完成的。
对象适配器是由类的关联关系纪念性耦合。

桥接模式

桥梁模式的重点在解耦。

定义

将抽象和实现解耦,使得两者可以独立的变化。
它使用了类间的聚合关系、继承、覆写等常用功能。解决了继承的缺点而提出的设计模式。

桥接模式通用类图

实现

抽象角色引用实现角色,或者说抽象角色的部分实现是由实现角色完成的。

Abstraction

抽象化角色,定义出该抽象角色的行为,同时保存一个对实现化角色的引用。

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

private Implementor imp;
//必须指定实现者
public Abstraction(Implementor imp){
this.imp = imp;
}

public void request(){
this.imp.doSomething();
}
public Implementor getImp(){
return imp;
}
}

RefindAbstraction

修正抽象化角色,引用实现化角色对抽象化角色进行修正。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class RefindAbstraction extends Abstraction{

public RefindAbstraction(Implementor imp){
super(imp);
}

@Override
public void request(){
//....
super.request();
super.getImp().doAnyThing();
}
}

Implementor

实现化角色,接口或者抽象类,定义角色必须的行为和属性。

1
2
3
4
public interface Implementor{
public void doSomething();
public void doAnyThing();
}

ConcreteImplementor

具体实现化角色,实现接口或者抽象类定义的方法和属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ConcreteImplementor1 implements Implementor{

public void doSomething(){
//
}
public void doAnyThing(){
//
}
}

public class ConcreteImplementor2 implements Implementor{

public void doSomething(){
//
}
public void doAnyThing(){
//
}
}

Use

1
2
3
Implementor imp = new ConcreteImplementor1();
Abstraction abs = new RefindAbstraction(imp);
abs.request();

应用

优点

  • 抽象和实现分离
    为了解决继承的缺点而提出的设计模式,在该模式下,实现可以不受抽象的约束,不再绑定到固定的抽象层次上。

  • 优秀的扩展能力

可以随意增加抽象和实现。

  • 实现细节对客户透明

由抽象层通过聚合关系完成了封装。

使用场景

  • 不希望或者不适用使用继承的场景
    继承层次太高,无法更细化设计颗粒等场景,需要考虑使用。
  • 接口或者抽象类不稳定的场景
    接口或者抽象类不稳定,经常变更时,使用桥梁模式。
  • 重用型要求高的场景
    设计的颗粒度粤西,被重用的可能性越大。
    采用继承受到父类的限制,不可能出现太细的粒度。

装饰器模式

定义

动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式相比生成子类更加灵活。

装饰器模式通用类图

实现

Component

Component 是一个接口或者抽象类,定义需要被装饰的原始类。

1
2
3
public abstract class Component{
public abstract void operate();
}

Concrete Component

Component 的实现类。

1
2
3
4
5
6
7
8
public class ConcreteComponent extends Component{

@Override
public void operate(){
//....
}

}

Decorator

抽象类,用于实现接口或者抽象方法。包含被修饰的Componnet属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class Decorator extends Component{

private Component component = null;

public Decorator(Component c){
this.component = c;
}
@Override
public void operate(){
this.component.operate();
}

}

Concrete Decorator

具体装饰角色

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
public class ConcreteDecorator1 extends Decorator{
public ConcreteDecorator1(Component component){
super(component);
}
private void method1(){
//....
}
public void operate(){
this.method1();
super.operate();
}
}


public class ConcreteDecorator2 extends Decorator{
public ConcreteDecorator2(Component component){
super(component);
}
private void method2(){
//....
}
public void operate(){
super.operate();
this.method2();
}
}

Use

1
2
3
4
Component c = new ConcreteComponent();
c = new ConcreteDecorator1(c);
c = new ConcreteDecorator2(c);
c.operate();

应用

优点

装饰类和被装饰类可以独立发展,而不会相互耦合。
装饰模式是继承关系的一个替代方案。
装饰模式可以动态地扩展一个实现类的功能。

缺点

当装饰多层时导致系统复杂度高。

观察者模式

观察者模式也叫发布订阅模式。

定义

定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并且被自动更新。

观察者模式通用类图

实现

Subject

定义被观察者必须实现的职责,必须能够动态的增加、取消观察者。
它一般是抽象类或者实现类,仅仅完成作为被观察者必须实现的职责,管理观察者并通知观察者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class Subject{
private Vector<Observer> obsVector = new Vector<Observer>();

public void addObserver(Observer o){
this.obsVector.add(o);
}

public void delObserver(Observer o){
this.obsVector.remove(o);
}

public void notifyObservers(){
for(Observer o : this.obsVector){
o.update();
}
}

ConcreteSubject

具体的被观察者,定义被观察者自己的业务,同时定义对哪些事件进行通知。

1
2
3
4
5
6
public class ConcreteSubject extends Subject{
public void doBusiness(){
//...
super.notifyObservers();
}
}

Observer

观察者接收到消息后,对接收到的信息进行处理。

1
2
3
public interface Observer{
public void update();
}

ConcreteObserver

具体的观察者,不同的观察者在处理消息的时候的具体定义。

1
2
3
4
5
public class ConcreteObserver implements Observer{
public void update(){
//do something when invoked.
}
}

Use

1
2
3
4
ConcreteSubject subject = new ConcreteSubject();
Observer obs = new ConcreteObserver();
subject.addObserver(obs);
subject.doBusiness();

应用

优点

  • 观察者和被观察之间是抽象耦合
    如论增加观察者还是被观察者都非常容易扩展
  • 建立了一套规则的触发机制

缺点

效率缺陷,考虑采用异步方式通知。

广播链的问题 A -> B -> C

扩展

Java中的观察者

java 提供了java.util.Observer接口 和 java.util.Observable 类。
被观察者继承java.util.Observable, 观察者实现java.util.Observer.
但是在生产环境中考虑到性能一般不会用这种方式来实现。

生产中的观察者

一般使用MQ来异步处理
本地的如Disruptor

中介者模式

主要为了降低模块内部之间类的相互引用,防止出现系统或者模块内部过度耦合。

中介者模式也叫调停者模式,一个对象要和N个对象交流特别混乱,这个时候加入一个中心,所有的类都和中心交流,这样就变成了星型拓扑结构。

星型拓扑结构

定义

用一个中介对象封装一些列的对象交互,中介者使各对象不需要显示地相互调用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

中介者模式通用类图

实现

Mediator

抽象中介者角色,定义统一的接口,用于各同事角色之间的通信。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class Mediator{
protected ConcreteColleague1 c1;
protected ConcreteColleague2 c2;
public ConcreteColleague1 getC1(){ return c1;}
public ConcreteColleague2 getC2(){ return c2;}
public void setC1(ConcreteColleague1 c1){
this.c1 = c1;
}
public void setC2(ConcreteColleague2 c2){
this.c2 = c2;
}

public abstract void doSomething1();
public abstract void doSomething2();
}

Concrete Mediator

具体中介者角色通过协调各同事角色实现协作行为,因为它必须依赖于各个同事角色。

1
2
3
4
5
6
7
8
9
10
public class ConcreteMediator extends Mediator{
public void doSomething1(){
super.c1.selfMethod1();
super.c2.selfMethod2();
}
public void doSomething2(){
super.c1.selfMethod1();
super.c2.selfMethod2();
}
}

Colleague

同事角色,每个同事角色都知道中介者角色,而且与其他同事角色通信时,一定要通过中介者角色协作。
一种是同事本身的行为叫自发行为,比如改变对象本身的状态,处理自己的行为,与其他同事类或者中介者
第二种是依赖行为,必须依赖中介者才能完成的行为。

1
2
3
4
5
6
public abstract class Colleague{
protected Mediator mediator;
public Colleague(Mediator mediator){
this.mediator = mediator;
}
}
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 ConcreteColleague1 extends Colleague{
public ConcreteColleague1(Mediator mediator){
super(mediator);
}

public void selfMethod1(){

}

public void depMethod1(){
super.mediator.doSomething1();
}
}

public class ConcreteColleague2 extends Colleague{
public ConcreteColleague2(Mediator mediator){
super(mediator);
}

public void selfMethod1(){

}

public void depMethod1(){
super.mediator.doSomething1();
}
}

应用

优点

减少类之间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者,减少了依赖,同时降低了类之间的耦合。

缺点

中介者模式的缺点是中介者会膨胀,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。

应用

机场调度中心

每一架飞机都和机场调度中心保持联系。

MVC框架

Controller做为一个中介者,把Model和View隔离开,协调MV协同工作,把M运行的结果和V代表的视图融合成一个前端可以展示的页面,减少MV的依赖关系。

媒体网关

中介服务

空对象模式

空对象模式时通过空代码实现一个接口或者抽象类的所有方法,以满足开发需求,简化程序。

定义

通过实现一个默认无意义的对象来避免null值的出现。为了避免程序中出现判断null的情况而产生的。

空对象模式通用类图

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Animal{
public void makeSound();
}

public class Dog implements Animal{
public void makeSound(){
System.out.println("Wang...");
}
}

public class NullAnimal import Animal{
public void makeSound(){}
}

解释器模式

解释器模式是一种按照规定语法进行解析的方案。

定义

给一门语言,定义它的文法的一种表示,并且定义一个解释器,该解释器使用该表示来解释语言中的句子。

比如Java语言,定义文法的表示是Java的语法。
javac编译器看作是解释器,解释器用来将Java“解释为” .class 格式的文件。

实现

AbstractExpression

抽象解释器,抽象解释器通常只有一个方法, 是生成语法集合的关键,每个语法集合完成指定语法的解析任务,通过递归调用的方式,最终由最小的语法但愿进行解析完成。
具体的解释任务由各个实现类完成。具体的解释器分别由TerminalExpression和Non-terminalExpression完成。

1
2
3
4
5
6
7
8
9
10
11
public abstract class Expression {

/**
* 解析公式和数值,其中var中的key是公式中的参数,value值是具体的数字。
* @param var
* @return
*/
public abstract int interpreter(HashMap<String,Integer> var);


}

TerminalExpression

终结符表达式
实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符的表达式,但是有多个实例,对应不同的终结符。表达式中每个终结符都在栈中产生了一个varExpression。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class VarExpression extends Expression {

private String key;

public VarExpression(String key) {
this.key = key;
}

@Override
public int interpreter(HashMap<String, Integer> var) {
return var.get(this.key);
}
}

NoneTerminalExpression

非终结符表达式
文法中每条规则都对应一个非终结表达式,具体到我们的例子就是加减法规则分别对应到AddExpression和SubExpression两个类,非终结表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。

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
public abstract class SymbolExpression extends Expression {

protected Expression left;

protected Expression right;

public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
}

public class AddExpression extends SymbolExpression {

public AddExpression(Expression left, Expression right) {
super(left, right);
}

@Override
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) + super.right.interpreter(var);
}
}

public class SubExpression extends SymbolExpression {

public SubExpression(Expression left, Expression right) {
super(left, right);
}

@Override
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}

Context

环境角色,

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
public class Calculator {

private Expression expression;

public Calculator(String expStr) {
Stack<Expression> stack = new Stack<>();
char[] charArray = expStr.toCharArray();

Expression left = null;
Expression right = null;

for (int i = 0; i < charArray.length; i++) {
switch (charArray[i]) {
case '+':
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new AddExpression(left, right));
break;
case '-':
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new SubExpression(left, right));
break;
default:
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
this.expression = stack.pop();
}

public int run(HashMap<String, Integer> var) {
return this.expression.interpreter(var);
}
}

Use

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args) throws IOException {
String expStr = getExpStr();
HashMap<String, Integer> var = getValue(expStr);
Calculator calculator = new Calculator(expStr);
System.out.println(calculator.run(var));
}

private static HashMap<String, Integer> getValue(String expStr) throws IOException {
HashMap<String, Integer> map = new HashMap<>();
for (char c : expStr.toCharArray()) {
if (c != '+' & c != '-') {
if (!map.containsKey(String.valueOf(c))) {
String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
map.put(String.valueOf(c), Integer.valueOf(in));
}
}
}
return map;
}

private static String getExpStr() throws IOException {
System.out.println("Input Expression");
return new BufferedReader(new InputStreamReader(System.in)).readLine();
}

应用

优点

扩展性良好,修改语法规则只需要修改相应的非终结符表达式就可以了。
如果扩展语法,则只要增加非终结符类就可以了。

缺点

解释器模式会引起类膨胀,每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可以能产生大量的类文件。
解释器模式采用递归调用方式,每个表达式需要知道最终的结果,必须一层层递归,无论是面向对象还是面向过程,递归都是在必要条件下才使用,导致调试复杂。

效率问题,解释器模式使用大量的循环和递归,效率时一个很大的问题。

使用场景

重复发生的问题可以解释器模式,多个应用服务器,每天产生大量的日志,需要对日志文件进行分析处理,由于各个服务器的日志格式不同,但是数据要素相同,按照解释器模式终结符表达式相同,非终结符表达式需要定制。

一个简单语法需要解释的场景,解释器模式一般用来解析表标准的字符集,如SQL语法分析。

注意事项

尽量不要在重要的模块中使用解释器模式,维护成本太高。尽量使用shell、JRuby、Groovy来替代。

扩展

成熟的工具
Expression4J
MESP: Math Expression String Parser、
Jep.

备忘录模式

定义

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

备忘录模式通用类图

实现

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);
}
}