以下内容均来自GeekBand极客班C++ 设计模式课程(李建忠老师主讲)
“行为变化”模式
在组件的构建过程中,组件行为的变化经常导致组件本身剧烈的变化。“行为变化”模式将组件的行为和组件本身进行解耦,从而支持组件行为的变化,实现两者之间的松耦合。
典型模式:
Command
Visitor
在软件构建过程中,由于需求的改变,某些类层次结构中(从基类到子类都要添加)常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来繁重的变更负担,甚至破坏原有设计
如何在不更改类层次结构的前提下,在运行时更具需要透明地为类层次结构上的各个类动态添加新的操作。从而避免上述问题?
《设计模式》GOF的定义:
表示一个作用于某对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)
该模式有一个缺点
Visitor和Element需要稳定,同时,Visitor类中的有关于Element子类的具体扩展行为,所以有一个大前提,Element的子类,必须是稳定的!
否则,一旦多一个Element的子类,那么visitor还需要打破“开放封闭原则”,所以访问者设计模式,要使用,需要满足一个大前提:Element的子类个数是稳定的
现在有一个基类Element
class Element{
public:
virtual void Func1() = 0;
virtual ~Element();
};
class ElementA : public Element{
public:
void Func1(){
}
};
class ElementB : public Element{
public:
void Func1(){
}
}
现在要在Element中增加行为
最直观的就行下面这种方式,增加一个Func2行为,每个子类中都要进行修改
class Element{
public:
virtual void Func1() = 0;
//增加行为 最直观的感受 就是在基类里面增加一行
virtual void Func2(int data) = 0;
virtual ~Element();
};
class ElementA : public Element{
public:
void Func1(){
}
//增加行为
void Func2(int data){
}
};
class ElementB : public Element{
public:
void Func1(){
}
//增加行为
void Func2(int data){
}
}
显然,上面的行为违背了“开放封闭原则”
直接在源码里面,而且是基类里面直接进行修改,显然不是很合适
下面看看visitor模式是如何解决上述问题的
class Visitor;
class Element{
public:
virtual void accept(Visitor & visitor) = 0;
virtual ~Element();
};
首先在Element中定义一个accept函数.
子类中实现这个函数
class ElementA : public Element{
public:
virtual void accept(Visitor & visitor){
visitor.visitElementA(*this);
}
};
class ElementB : public Element{
public:
virtual void accept(Visitor & visitor){
visitor.visitElementB(*this);
}
};
下面我们看看visitor类,现在是预设,将来会出现新的操作
class Visitor{
public:
virtual void visitElementA(ElementA & element) = 0;
virtual void visitElementB(ElementB & element) = 0;
virtual ~Visitor(){}
};
下面是要正式添加的新的操作
//添加操作1
class Visitor1 : public Visitor{
public:
virtual void visitElementA(ElementA & element){
cout<< "Visitor1 is processing ElementA" <
直接增加新的类,继承于visitor即可
当我们要让Element1或者Element2去执行新的行为,该怎么办呢?
Visitor2 visitor;
ElementB element;
element.accept(visitor);
accept是个虚函数,element类型为ElementB,所以执行的是ElementB中的accept函数
以上为伪代码,显然是可以将其转化为指针,进行多次的,多态辨析
class Visitor;
class Element{
public:
virtual void accept(Visitor * visitor) = 0;
virtual ~Element(){}
};
class ElementA : public Element{
public:
virtual void accept(Visitor * visitor){
visitor->visitElementA(this);
}
virtual ~ElementA(){}
};
class ElementB : public Element{
public:
virtual void accept(Visitor * visitor){
visitor->visitElementB(this);
}
virtual ~ElementB(){}
};
注意观察,上述内容是不会去改变的,是已经设计好的,不会发生变化。扩展的部分全部留在后面
class Visitor{
public:
virtual void visitElementA(ElementA * element) = 0;
virtual void visitElementB(ElementB * element) = 0;
virtual ~Visitor(){}
};
//添加操作1
class Visitor1 : public Visitor{
public:
virtual void visitElementA(ElementA * element){
cout<< "Visitor1 is processing ElementA" <
Element * element = new ElementB();//第一次多态辨析,行为的发出者是谁?
Visitor2 * visitor = new Visitor2();//第二次多态辨析,发出的行为到底是什么行为?
element->accept(visitor);
Visitor模式通过所谓双重分发(double dispatch)来实现在不更改(不添加新的操作—编译时)Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作(支持变化)。
所谓双重分发即Visitor模式中间包括了两个多态分发:第一个是accept方法的多态辨析,第二个是visitorElementX方法的多态辨析。
Visitor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变,因此Visitor模式适用于“Element类层次结构稳定,而其中的操作却经常面临频繁改动”