在软件开发中,经常需要用到多态,继承等机制,下面是很常见的一段代码
class Brand
{
public:
virtual void productShoe() = 0;
}
class Nike : public Brand
{
public:
virtual void productShoe()
{
//createAj1();
}
};
class Adidas : public Brand
{
public:
virtual void productShoe()
{
//createYeezy();
}
};
基类品牌有一个生产鞋子的方法,子类继承并重写(override)。这种类的层次结构中常常会需要增加新的行为,比如我们需要生产衣服,我们一般会这么改。
class Brand
{
public:
virtual void productShoe() = 0;
virtual void productClothes() = 0;
}
class Nike : public Brand
{
public:
virtual void productShoe() override
{
//createAj1();
}
virtual void productClothes()
{
//createNSW();
}
};
class Adidas : public Brand
{
public:
virtual void productShoe()
{
//createYeezy();
}
virtual void productClothes()
{
//createOriginals();
}
};
问题来了,这样改会更改抽象基类,但是抽象应该是稳定的(依赖倒置),并且也不符合开放封闭原则(对修改封闭)。
这样,我们的访问者模式就应运而出了!
(当然,在这里我们可以默认这整个类的层次结构是不会改变的,也就是就这么几个子类)
访问者模式是为了解决行为变化的,和命令者模式(command)一起归为行为变化模式。
我们可以将代码用访问者模式重构成如下代码
class Visitor;
class Brand
{
public:
virtual void accept(Visitor& visitor) = 0;
}
class Nike : public Brand
{
public:
virtual void accept(Visitor& visitor)
{
visitor.visitNike(*this);
}
};
class Adidas : public Brand
{
public:
virtual void accept(Visitor& visitor)
{
visitor.visitAdidas(*this);
}
};
class Visitor
{
public:
virtual void visitNike(Nike& nike) = 0;
virtual void visitAdidas(Adidas& adidas) = 0;
};
class ShoeVisitor : public Visitor
{
public:
virtual void visitNike(Nike& nike)
{
//nike.createAj();
};
virtual void visitAdidas(Adidas& adidas)
{
//adidas.createYeezy();
}
};
这样,我们调用的时候只需这样:
Adidas adidas;
ShoeVisitor visitor;
adidas.accept(visitor); //double dispatch
注意,注释的地方的double dispatch的含义是两次多态辨析,意思是这里使用了两次多态调用。
首先accept方法是继承自基类Brand,其次accept里面的visitAdidas的方法也是来自原基类Visitor的虚函数,这又是一次多态。
这样,我们的功能(行为)就可以都写在visitNike里面,而不必破坏原有的类结构。
缺点:
我们从上面代码可以看出,基类Visitor是一个抽象基类,抽象就应该是稳定的,而基类Visitor中的两个方法都是依赖于Brand的两个子类Nike和Adidas,这就说明这个体系中,Brand和Brand的两个子类也应该是稳定的,然而这个条件很难达到。
ps:一般这个模式都是运用在一种固定的类结构中(比如树形结构中),对不同的对象(不同类型的树节点)进行不同的操作(可以是处理数据节点,也可以是遍历父节点),这里子类的类型必须要是稳定的(树节点的类型是稳定的)。