行为型模式--备忘录

1、意图

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

2、结构

 

 3、参与者

  Memento:备忘录,存储原发器对象的内部状态。原发器根据需要决定备忘录存储原发器哪些内部状态;

       防止原发器以外的其他对象访问备忘录。备忘录实际上有两个接口,管理者(caretaker)只能看到备忘录的窄接口——它只能将备忘录传递给其他对象。相反,原发器能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。理想的情况时只允许生成本备忘录的那个原发器访问本备忘录的内部状态。

  Originator:原发器,创建一个备忘录,用以记录当前时刻它的内部状态;使用备忘录恢复内部状态;

  Caretaker:负责人,负责保存好备忘录;不能对备忘录的内容进行操作或检查;

4、适用性

  在以下情况下使用备忘录模式:

  必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态;

  如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性;

5、代码示例

  场景:考虑一个图形编辑器,它支持图形对象间的连线。用户可用一条直线连接两个矩形,而当用户移动任意一个矩形时,这两个矩形仍能保持连接。在移动过程中,编辑器自动伸展这条直线以保持该连接。

  一个保持对象间连接关系的方法是使用一个约束解释系统。我们可将这一功能封装在一个ConstraintSolver对象中。ConstraintSolver在连接生成时,记录这些连接并产生描述它们的数学方程。当用户生成一个连接或修改图形时,ConstraintSolver就求解这些方程。并根据它的计算结果重新调整图形,使各个对象保持正确的连接。

// 使用MoveCommand命令对象来执行或撤销一个图形对象从一个位置到另一个位置的移动变换

class Graphic;    // base class for graphical objects in the graphical editor 

// 命令对象存储它的目标、移动的距离和一个ConstraintSolverMemento的实例,这是一个包含约束解释器状态的备忘录
class MoveCommand
{
public:
    MoveCommand(Graphic* target, const Point& delta); 
    void Execute(); 
    void Unexecute(); 
private:
    ConstraintSolverMemento* _state; 
    Point _delta; 
    Graphic* _target;
};
// 连接约束
class ConstraintSolver
{
public:
    static ConstraintSolver* Instance();
    
    // 关键函数,解释那些由AddConstraint操作注册的约束
    void Solve(); 
    
    void AddConstraint(Graphic* startConnection, Graphic* endConnection);
    void RemoveConstraint(Graphic* startConnection, Graphic* endConnection);
    
    // 将自身状态存储在外部的一个ConstraintSolverMemento实例中
    ConstraintSolverMemento* CreateMemento(); 
    
    // 使约束解释器返回到先前某个状态
    void SetMemento(ConstraintSolverMemento*); 
private:
    // nontrivial state and operations for enforcing
    // connectivity semantics
};
class ConstraintSolverMemento
{
public:
    virtual ~ConstraintSolverMemento(); 
private:
    friend class Constraintsolver; 
    ConstraintSolverMemento();
    
    // private constraint solver state
};
// 给定这些接口,可以实现MoveCommand的成员函数Execute和Unexecute如下:
void MoveCommand::Execute ()
{
    // Execute在移动图形前先获取一个ConstraintSolverMemento备忘录。
    ConstraintSolver* solver = ConstraintSolver::Instance();
    _state = solver->CreateMemento();//create a memento
    _target->Move(_delta); 
    solver->So1ve();
}

void MoveCommand::Unexecute ()
{
    // Unexecute先将图形移回,再将约束解释器的状态设回原先的状态,并最后让约束解释器解释这些约束。
    ConstraintSolver* solver = ConstraintSolver::Instance();
    _target->Move(-_delta); 
    solver->SetMemento(_state);//restore solver state 
    solver->So1ve();
}

6、总结

  备忘录模式用来保存对象的某一时刻的部分或完整状态,以待后续需要撤销动作时进行恢复操作。

  备忘录有宽接口和窄接口的概念,原发器能访问宽接口,而除原发器外的其他类包括Caretaker仅能访问窄接口。

  备忘录的在C++中实现的重点是把原发器Originator类设置成备忘录Memento类的友元,备忘录的接口为私有或保护,这样使得其他类或Caretaker类无法访问备忘录只能传递备忘录对象,而仅有原发器才能访问。

posted @ 2022-05-04 15:40  流翎  阅读(22)  评论(0编辑  收藏  举报