备忘录模式与访问者模式

备忘录模式

Memento模式也叫备忘录模式,是行为模式之一,它的作用是保存对象(原生对象)的内部状态到备忘录,并在需要的时候通过caretaker(保存了备忘录)提取备忘录的内容(undo/rollback)恢复原生对象以前的状态。
角色及职责
这里写图片描述
这里写图片描述
Originator(原生者)
需要被保存状态以便恢复的那个对象。
Memento(备忘录)
该对象由Originator创建,主要用来保存Originator的内部状态。
Caretaker(管理者)
负责在适当的时间保存/恢复Originator对象的状态。
适用于:
  在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样就可以将以后的对象状态恢复到先前保存的状态。
  适用于功能比较复杂的,但需要记录或维护属性历史的类;或者需要保存的属性只是众多属性中的一小部分时Originator可以根据保存的Memo还原到前一状态。
案例
如果一个对象需要保存状态并可通过undo或rollback等操作恢复到以前的状态时,可以使用Memento模式。

1)一个类需要保存它的对象的状态(相当于Originator角色)
2)设计一个类,该类只是用来保存上述对象的状态(相当于Memento角色)
3)需要的时候,Caretaker角色要求Originator返回一个Memento并加以保存
4)undo或rollback操作时,通过Caretaker保存的Memento恢复Originator对象的状态

/*******************************
 *某个对象也许在将来有一天会恢复到现如今的状态,
 *那么就必须找一个备忘录存储现如今的状态,以便找得到恢复的依据
 *
 *原生类需要依赖备忘录存储自己的信息
 * --所以需要创建这个备忘录--函数返回值是备忘录
 *
 * 备忘录和原生类的属性几乎一样--很显然是为了记录原生类啊
 *
 *管理者需要保存备忘录的内容--实际上保存的就是原生对象的内部状态
 * --需要备忘录做成员变量(关联)
 * --构造函数需要备忘录做参数(依赖)
 * --设置函数需要备忘录做参数
 *
*******************************/


#include <iostream>
#include <string>
using namespace std;


/*备忘录--存放源对象的内部状态--不打破封装性*/
class Memto
{
public:
    Memto(string name,int age)//构造函数创建备忘录
    {
        m_name = name;
        m_age = age;
    }

    string getName()//获取缓存的属性--姓名
    {
        return m_name;
    }

    int getAge()//获取缓存的属性--年龄
    {
        return m_age;
    }


private:
    /*具有和原生类一样的属性*/
    string m_name;
    int m_age;
};


/*待保存的原生类--能够创建备忘录*/
class Person
{
public:
    Person(string name,int age)//构造函数构造原生对象
    {
        m_name = name;
        m_age = age;
    }

    string getName()//获取属性
    {
        return m_name;
    }

    int getAge()//获取属性
    {
        return m_age;
    }

    void setName(string name)//设置属性
    {
        m_name = name;
    }

    void setAge(int age)//设置属性
    {
        m_age = age;
    }

    Memto * createMemto()//创建一个备忘录--目的是传递给管理者
    {
        return new Memto(m_name,m_age);
    }

    void setMemto(Memto * memto)//恢复内部状态--根据之前的备忘录恢复自己的状态--这个备忘录从管理者获取
    {
        m_name = memto->getName();
        m_age = memto->getAge();
    }


    void printPerson()//打印信息
    {
        cout<<"name : "<<m_name << "age : "<<m_age<<endl;
    }


private:

    /*自己的私有属性--备忘录需要具有相关的属性--以便记录*/
    string m_name;
    int m_age;
};


/*管理者*/
class CareTaker
{
public:
    CareTaker(Memto *mem)//利用备忘录构造管理者--接收已创建好的备忘录--保存备忘录
    {
        m_mem = mem;
    }

    /*根据已有的备忘录设置自己的备忘录成员--实质上就是进行保存备忘录的操作*/
    void setmem(Memto *mem)
    {
        m_mem = mem;
    }

    /*获取备忘录--将备忘录提供给想要恢复状态的原生对象*/
    Memto *getMem()
    {
        return m_mem;
    }

private:
    Memto * m_mem;//保存的备忘录
};


/*测试案例*/
int main()
{

    Person * p = new Person("simon",22);//原生对象
    CareTaker * care = NULL;//管理者

    p->printPerson();

    care = new CareTaker(p->createMemto());//原生对象创建备忘录
                                            //管理者保存该备忘录

    p->setAge(42);//原生对象内部状态发生改变
    p->printPerson();

    /*管理者提供自己已经保存的备忘录
    *原生对象根据他提供的备忘录进行恢复
    */
    p->setMemto(care->getMem());
    p->printPerson();


    return 0;
}

访问者模式

Visitor模式也叫访问者模式,是行为模式之一,它分离对象的数据和行为,使用Visitor模式,可以不修改已有类的情况下,增加新的操作角色和职责。
访问者模式特点:
访问者模式优点是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。
那访问者模式的缺点是是增加新的数据结构变得困难了
角色和职责
这里写图片描述
这里写图片描述
抽象访问者(Visitor)角色:声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口。
具体访问者(ConcreteVisitor)角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
抽象节点(Element)角色:声明一个接受操作,接受一个访问者对象作为一个参量。
具体节点(ConcreteElement)角色:实现了抽象元素所规定的接受操作。
结构对象(ObiectStructure)角色:有如下的一些责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚集,如列(List)或集合(Set)。
适用于:
把数据结构 和 作用于数据结构上的操作 进行解耦合;
适用于数据结构比较稳定的场合
优缺点
访问者模式有如下的优点:
1,访问者模式使得增加新的操作变得很容易。如果一些操作依赖于一个复杂的结构对象的话,那么一般而言,增加新的操作会很复杂。而使用访问者模式,增加新的操作就意味着增加一个新的访问者类,因此,变得很容易。
2,访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。
3,访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。迭代子只能访问属于同一个类型等级结构的成员对象,而不能访问属于不同等级结构的对象。访问者模式可以做到这一点。
4,积累状态。每一个单独的访问者对象都集中了相关的行为,从而也就可以在访问的过程中将执行操作的状态积累在自己内部,而不是分散到很多的节点对象中。这是有益于系统维护的优点。

访问者模式有如下的缺点:
1,增加新的节点类变得很困难。每增加一个新的节点都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作。
2,破坏封装。访问者模式要求访问者对象访问并调用每一个节点对象的操作,这隐含了一个对所有节点对象的要求:它们必须暴露一些自己的操作和内部状态。不然,访问者的访问就变得没有意义。由于访问者对象自己会积累访问操作所需的状态,从而使这些状态不再存储在节点对象中,这也是破坏封装的。
案例
比如有一个公园,有一到多个不同的组成部分;该公园存在多个访问者:清洁工A负责打扫公园的A部分,清洁工B负责打扫公园的B部分,公园的管理者负责检点各项事务是否完成,上级领导可以视察公园等等。也就是说,对于同一个公园,不同的访问者有不同的行为操作,而且访问者的种类也可能需要根据时间的推移而变化(行为的扩展性)。
  根据软件设计的开闭原则(对修改关闭,对扩展开放),我们怎么样实现这种需求呢?

#include <iostream>
#include <string>
#include <list>
using namespace std;

class ParkElement;//前置声明


/*抽象访问者*/
class Visitor
{
public:
    virtual void visit(ParkElement * park_elem) = 0;
};

/*抽象节点*/
class ParkElement
{
public:
    virtual void accept(Visitor * v) = 0;
    string getName()
    {
        return  m_name;
    }
protected:
    string m_name;
};

/*具体节点*/
class ParkA : public ParkElement
{
public:
    ParkA()
    {
        m_name = "ParkA";
    }

    virtual void accept(Visitor * v)
    {
        v->visit(this);
    }
};

/*具体节点*/
class ParkB : public ParkElement
{
public:
    ParkB()
    {
        m_name = "ParkB";
    }

    virtual void accept(Visitor * v)
    {
        v->visit(this);
    }
};


/*具体访问者--不同访问者代表不同的操作*/
class VisitorA : public Visitor
{
public:
    virtual void visit(ParkElement * park_elem)
    {
        cout<<"VisitorA Do"<<park_elem->getName()<<endl;
    }
};

/*具体访问者--不同访问者代表不同的操作*/
class VisitorB : public Visitor
{
public:
    virtual void visit(ParkElement * park_elem)
    {
        cout<<"VisitorB Do"<<park_elem->getName()<<endl;
    }
};

/*具体节点--代表所有节点的集合--是一个比较特殊的具体节点*/
class Park: public ParkElement
{

public:
    Park()
    {
        m_list.clear();
    }

    void setParkElement(ParkElement * pe)
    {
        m_list.push_back(pe);
    }
    virtual void accept(Visitor * v)//遍历访问
    {
        for(list<ParkElement*>::iterator it = m_list.begin();it != m_list.end();it++)
        {
            v->visit(*it);
        }

    }

private:
    list<ParkElement*> m_list;//公园的每一部分--应该让每一部分都让管理者访问
};

/*具体访问者--代表一个管理员--针对所有的具体节点都可以进行操作*/
class Manager:public Visitor
{
public:
    virtual void visit(ParkElement * park_elem)
    {
        cout<<"Manager Do"<<park_elem->getName()<<endl;
    }
private:

};

/*一个具体节点接受一个具体访问者--低效--没有完全实现数据结构和操作的完全分离*/
void play()
{
    Visitor *vA = new VisitorA;
    Visitor *vB = new VisitorB;

    ParkElement *pA = new ParkA;
    ParkElement *pB = new ParkB;

    pA->accept(vA);
    pB->accept(vB);

    delete vA;
    delete vB;
    delete pA;
    delete pB;
}

/*实现了多态
实现了数据结构和操作的完全分离*/
void play02()
{
    Visitor *vM = new Manager;


    ParkElement *pA = new ParkA;
    ParkElement *pB = new ParkB;
    Park * park = new Park;

    park->setParkElement(pA);
    park->setParkElement(pB);

    park->accept(vM);

    delete vM;
    delete park;
    delete pA;
    delete pB;
}

/*测试案例*/
int main()
{
    //play();
    play02();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值