背景
在软件构建的过程中,由于需求的变化会导致所有子类都要更新,在不改变原始框架的情况下如何扩展?
例子
比如说有个水果基类,下面有苹果,橘子,香蕉, 之前的代码一般是这样写的,比如说剥皮这个函数,每个具体水果的方式不一样,会有自己的实现方式。由于需求一直增加的,可以预见到以后的还会有榨汁,油炸,刺身等各种操作,如果每次去改源代码就不行,不符合开闭原则。
//berfore
class Fruit
{
public:
virtual void Func_1();
virtual void Func_2();
virtual void Func_3();
}
class Apple : public Fruit
{
public:
virtual void Func_1() override;
virtual void Func_2() override;
virtual void Func_3() override;
}
class Orange : public Fruit
{
public:
virtual void Func_1() override;
virtual void Func_2() override;
virtual void Func_3() override;
}
新的方式
//after
class Fruit
{
public:
virtual void accept(Visitor& vistor) = 0;
}
class Apple : public Fruit
{
public:
void accept(Vistor& vistor) override
{
vistor.visit(*this);
//vistor.visitApple(*this);
}
}
class Orange : public Fruit
{
public:
void accept(Vistor& vistor) override
{
vistor.visit(*this);
//vistor.visitOrange(*this);
}
}
class Vistor
{
public:
//可以用函数重载或者用不同函数名
virtual void visit(Apple& a);
virtual void visit(Orange& a);
//virtual void visitApple(Apple& a);
//virtual void visitOrange(Orange& a);
}
//----------------------------------------
//----------------------------------------
//新添加的操作
class Func_1 : public Vistor
{
public:
virtual void visit(Apple& a) override;
virtual void visit(Orange& a) override;
}
int main
{
auto Func_1 = new Func_1();
auto apple = new Apple();
apple->accept(*Func_1);
}
这个设计模式有一个缺陷,就是你在使用这个类的时候要保证这个类的子类个数是确定的。
总结
为什么不用第一种方式,假如你是一个厨师,当下的水果可以煎炸炒,当时你预期未来还有人去创新新的做法,那么你就可以用visit模式,毕竟水果种类短期几百年内很难进化出新品种。
题外话,装饰模式其实也是一个不改变原始数据结构添加新的行为,其实还是有点不同,一般装饰者模式是对某个行为再加个预处理或者处理,比如炸行为,可以炸之前裹点面包糠,这种可以用装饰模式,因为在装饰模式实现的时候,需要用public来继承炸接口,所以装饰模式结束后,理论上新的接口并没有增加,只是对原来的炸行为上加了预处理。