第二十八章 男人和女人--访问者模式
28.1 男人和女人
男人成功时,背后多半有一个伟大的女人。
女人成功时,背后多半有一个伟大的男人。
男人失败时,闷头喝酒,谁也不用劝。
女人失败时,眼泪汪汪,谁也劝不了。
男人恋爱时,凡是不懂也要装懂。
女人恋爱时,遇事懂也装作不懂。
男女对比这么多的原因就是因为人类在性别上就只有男人和女人两类。
28.2 最简单的编程实现
男人和女人应该继承人这样一个抽象类。所谓的成功失败或恋爱都是指人的一个状态,是一个属性。
28.3 简单的面向对象实现
person.h
#pragma once
#include <string>
#include <iostream>
typedef enum _PersonAction
{
eSucceed, //成功
eFailed,
eAmative //恋
}PersonAction;
class Person
{
public:
void setAction(PersonAction act)
{
m_action = act;
};
PersonAction GetAction(void)
{
return m_action;
};
virtual void GetConClusion(void) = 0;
protected:
PersonAction m_action;
};
class Man :public Person
{
public:
void GetConClusion(void)
{
if (m_action == eSucceed)
{
std::cout << "成功男人背后多半有一个伟大的女人" << std::endl;
}
else if (m_action == eFailed)
{
std::cout << "失败男人蒙头喝酒谁也不用劝" << std::endl;
}
else if (m_action == eAmative)
{
std::cout << "恋爱凡是不懂也要装懂" << std::endl;
}
};
};
class Woman :public Person
{
public:
void GetConClusion(void)
{
if (m_action == eSucceed)
{
std::cout << "成功女人背后多半有一个不成功的男人" << std::endl;
}
else if (m_action == eFailed)
{
std::cout << "失败女人眼泪汪汪谁也不用劝" << std::endl;
}
else if (m_action == eAmative)
{
std::cout << "恋爱凡是遇懂事也说不懂" << std::endl;
}
};
};
客户端
#include "Person.h"
#include <list>
int _tmain(int argc, _TCHAR* argv[])
{
std::list<Person*> listPerson;
Person* man1 = new Man();
man1->setAction(eSucceed);
Person* man2 = new Man();
man2->setAction(eFailed);
listPerson.push_back(man1);
listPerson.push_back(man2);
Person* woman1 = new Woman();
woman1->setAction(eSucceed);
Person* woman2 = new Woman();
woman2->setAction(eFailed);
listPerson.push_back(woman1);
listPerson.push_back(woman2);
std::list<Person*>::iterator iter = listPerson.begin();
while (iter != listPerson.end())
{
(*iter)->GetConClusion();
++iter;
}
iter = listPerson.begin();
while (iter != listPerson.end())
{
delete (*iter);
++iter;
}
listPerson.clear();
return 0;
}
在男人和女人类当中的那些if..else..很是碍眼吗?其实在这里关键在于人就只分男人和女人,这个性别的分类是稳定的,所以可以在状态类中,增加"男人反应"和"女人反应"两个方法,方法个数也是稳定的,不会很容易的发生变化。每一种具体状态都继承状态抽象类,实现两个反应的方法。
首先在客户程序中将具体状态作为参数传递给"男人"类完成一次分派,然后"男人"类调用作为参数的具体状态中的方法"男人反应",同时将自己this作为参数传递进去。这便完成了第二次分派。
这样做的好处就是使得我只需要增加一个"状态"子类,就可以在客户端调用来查看,不需要改动其他任何类的代码。
#pragma once
#include "Action.h"
class Person
{
public:
virtual void Accept(Action& visitor) = 0;
};
class Man :public Person
{
public:
void Accept(Action& visitor)
{
visitor.GetManConclusion(this);
};
};
class Woman :public Person
{
public:
void Accept(Action& visitor)
{
visitor.GetWomanConclusion(this);
};
};
Action.h
#pragma once
#include <iostream>
class Man;
class Woman;
class Action
{
public:
virtual void GetManConclusion(Man* concreteElementA) = 0;
virtual void GetWomanConclusion(Woman* concreteElementB) = 0;
};
class Success :public Action
{
public:
void GetManConclusion(Man* concreteElementA)
{
std::cout << "成功男人背后多半有一个伟大的女人" << std::endl;
};
void GetWomanConclusion(Woman* concreteElementB)
{
std::cout << "成功女人背后多半有一个不成功的男人" << std::endl;
};
};
class Failed :public Action
{
public:
void GetManConclusion(Man* concreteElementA)
{
std::cout << "失败男人蒙头喝酒谁也不用劝" << std::endl;
};
void GetWomanConclusion(Woman* concreteElementB)
{
std::cout << "失败女人眼泪汪汪谁也不用劝" << std::endl;
};
};
class Amativeness :public Action
{
public:
void GetManConclusion(Man* concreteElementA)
{
std::cout << "恋爱男人凡是不懂也要装懂" << std::endl;
};
void GetWomanConclusion(Woman* concreteElementB)
{
std::cout << "恋爱女人凡是遇懂事也说不懂" << std::endl;
};
};
class Marriage :public Action
{
public:
void GetManConclusion(Man* concreteElementA)
{
std::cout << "男人感叹到:恋爱游戏终结." << std::endl;
};
void GetWomanConclusion(Woman* concreteElementB)
{
std::cout << "女人感叹到:爱情长跑路漫漫,婚姻保险保平安" << std::endl;
};
};
ObjectStructre.h
#pragma once
#include "Person.h"
#include <list>
class ObjectStructre
{
public:
//增加
void Attach(Person* person)
{
m_elements.push_back(person);
};
//移除
void Detach(Person* person)
{
m_elements.remove(person);
};
//查看显示
void Display(Action& visitor)
{
std::list<Person*>::iterator iter = m_elements.begin();
while (iter != m_elements.end())
{
(*iter)->Accept(visitor);
iter++;
}
};
private:
std::list<Person*> m_elements;
};
客户端
#include "ObjectStructre.h"
int _tmain(int argc, _TCHAR* argv[])
{
ObjectStructre* o = new ObjectStructre();
o->Attach(new Man());
o->Attach(new Woman());
Success succ;
Failed fail;
Amativeness amative;
Marriage marriage;
//成功时的反应
o->Display(succ);
//失败时的反应
o->Display(fail);
//恋爱时的反应
o->Display(amative);
//结婚时的反应
o->Display(marriage);
return 0;
}
28.5 访问者模式
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
#pragma once
#include <string>
class Visitor;
class Element
{
public:
virtual void Accept(Visitor& visitor) = 0;
virtual std::string GetName(void) = 0;
};
class ConcreteElementA :public Element
{
public:
void Accept(Visitor& visitor);
std::string GetName(void)
{
return "ConcreteElementA";
};
};
class ConcreteElementB :public Element
{
public:
void Accept(Visitor& visitor);
std::string GetName(void)
{
return "ConcreteElementB";
};
};
#include "Element.h"
#include "visitor.h"
void ConcreteElementA::Accept(Visitor& visitor)
{
visitor.VisitorConcreteElementA(this);
};
void ConcreteElementB::Accept(Visitor& visitor)
{
visitor.VisitorConcreteElementB(this);
};
#pragma once
#include <iostream>
#include "Element.h"
class Visitor
{
public:
virtual void VisitorConcreteElementA(ConcreteElementA* elem) = 0;
virtual void VisitorConcreteElementB(ConcreteElementB* elem) = 0;
};
class ConcreteVisitor1 :public Visitor
{
public:
void VisitorConcreteElementA(ConcreteElementA* elem)
{
std::cout << "ConcreteVisitor1被" << elem->GetName()<<"购买" << std::endl;
};
void VisitorConcreteElementB(ConcreteElementB* elem)
{
std::cout << "ConcreteVisitor1被" << elem->GetName()<<"购买" << std::endl;
};
};
class ConcreteVisitor2 :public Visitor
{
public:
void VisitorConcreteElementA(ConcreteElementA* elem)
{
std::cout << "ConcreteVisitor2被" << elem->GetName()<<"购买" << std::endl;
};
void VisitorConcreteElementB(ConcreteElementB* elem)
{
std::cout << "ConcreteVisitor2被" << elem->GetName()<<"购买" << std::endl;
};
};
#include "ObjectStructre.h"
#include "Visitor.h"
int _tmain(int argc, _TCHAR* argv[])
{
ObjectStructre* o = new ObjectStructre();
o->Attach(new ConcreteElementA());
o->Attach(new ConcreteElementB());
ConcreteVisitor1 v1;
ConcreteVisitor2 v2;
o->Display(v1);
o->Display(v2);
return 0;
}
在这里Element就是我们的"人"类,而ConcreteElementA和ConcreteElementB就是"男人"和"女人",Visitor就是我们写的状态类,具体的ConcreteVisitor就是那些"成功","失败","恋爱"等等状态。至于ObjectStructure就是"对象结构"类了。
访问者模式适用于数据结构相对稳定的系统。它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。访问者模式的目的是要把处理从数据结构分离出来。很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的。因为访问者模式使得算法操作的增加变得容易。
访问者模式的缺点其实也就是使增加新的数据结构变得困难了。当然这里适用是因为人类性别这样的数据结构是稳定的。
28.7 比上不足,比下有余
访问者模式的能力和复杂性是把双刃剑,只有当你真正需要他的时候,才考虑使用它。