Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 访问者设计模式和多层类层次结构_C++_Design Patterns_Visitor Pattern - Fatal编程技术网

C++ 访问者设计模式和多层类层次结构

C++ 访问者设计模式和多层类层次结构,c++,design-patterns,visitor-pattern,C++,Design Patterns,Visitor Pattern,我与相关访客共有五节课: struct访问者 { virtual~Visitor()=默认值; 虚拟无效访问(A&{} 虚拟无效访问(B&{} 虚拟无效访问(C&{} 虚拟无效访问(D&{} 虚拟无效访问(E&{} }; 结构A { virtual~A()=默认值; 虚拟无效接受(访问者&v){v.visit(*this);} }; 结构B:A{void accept(Visitor&v)重写{v.visit(*this);}; 结构C:A{void accept(Visitor&v)重写{v.

我与相关访客共有五节课:

struct访问者
{
virtual~Visitor()=默认值;
虚拟无效访问(A&{}
虚拟无效访问(B&{}
虚拟无效访问(C&{}
虚拟无效访问(D&{}
虚拟无效访问(E&{}
};
结构A
{
virtual~A()=默认值;
虚拟无效接受(访问者&v){v.visit(*this);}
};
结构B:A{void accept(Visitor&v)重写{v.visit(*this);};
结构C:A{void accept(Visitor&v)重写{v.visit(*this);};
结构D:C{void accept(Visitor&v)重写{v.visit(*this);};
结构E:C{void accept(Visitor&v)重写{v.visit(*this);};
所有实例都将由用户代码在尽可能高的抽象级别上查看,因此它们都将被视为
A&
。用户代码需要执行两种类型的操作:

  • 如果实例的类型恰好是
    C
  • 如果实例类型为
    C
    或其任何子类型(即
    D
    E
    ),则打印
    “我是C”
  • 操作1的实现非常简单,在基础设施就绪的情况下,几乎可以从访问者设计模式框中实现:

    struct OperationOne : Visitor
    {
        void visit( C& ) override { std::cout << "I am C" << std::endl; }
    };
    

    问题是:对于第二个操作,假设我们不想重复
    D
    E
    的打印代码,则整个基础结构不再工作:

    struct OperationTwo : Visitor
    {
        void visit( C& ) override { std::cout << "I am C" << std::endl; }
        void visit( D& ) override { std::cout << "I am C" << std::endl; }
        void visit( E& ) override { std::cout << "I am C" << std::endl; }
    };
    
    这样,如果层次结构发生更改,我们将出现编译错误,因为在尝试传播接受的访问者时,编译器将再也找不到基类

    这就是说,我们现在可以编写第二个operation visitor,这次我们不需要为
    D
    E
    复制打印代码:

    struct OperationTwo : Visitor
    {
        void visit(C&) override { std::cout << "I am C" << std::endl; }
    }
    

    但是等待:
    OperationOne
    OperationTwo
    代码完全相同!这意味着,通过改变第二次运营的基础设施,我们基本上打破了第一次运营。事实上,现在还有
    操作一个
    将打印三次字符串
    “I am C”


    要使
    操作一个
    操作二个
    看起来单独工作,可以做些什么?我是否需要将访问者设计模式与其他设计模式相结合,或者我是否完全不需要使用访问者?

    您可以使用以下内容作为访问者,这些内容将以重载解析方式发送:

    模板
    结构重载访问者:访问者
    {
    F;
    无效访问(A&A)覆盖{f(A);}
    无效访问(B&B)覆盖{f(B);}
    无效访问(C&C)覆盖{f(C);}
    无效访问(D&D)覆盖{f(D);}
    无效访问(E&E)覆盖{f(E);}
    };
    
    然后

    struct IAmAC
    {
        void operator()( C& ) { std::cout << "I am C" << std::endl; }
        void operator()( A& ) {} // Fallback
    };
    
    using OperationTwo = OverloadVisitor<IAmAC>;
    
    struct IAmAC
    {
    
    void operator()(C&){std::cout第一个操作在设计方面没有多大意义。为什么你想知道任何东西的确切类型?它会影响LSP。(我不是在谈论诸如序列化之类的基础类型代码,而是abiut应用程序级逻辑)@n.m对我来说,这是有道理的。我们使用这些访问者作为场景图的遍历器,用于各种操作。因此,我知道这两种情况都是可以想象的。(因此,我对这个问题的兴趣可能会得到回报。);-)这可能是有意义的…就目前而言。这种设计通常很脆弱,在层次结构中没有或不太重要的变化的情况下会崩溃。如果你决定将C分成两个类,或者让它成为一个带有具体子类的抽象类,或者诸如此类,那么你的设计应该继续有效。如果它对你有效,当然欢迎你使用它。@n、 m我部分同意。抽象基类的技巧确实是我曾经用作解决方案的东西。另一方面:场景图上的必要操作会随着时间的推移而演变(随着需求和用例数量的增加)很难预见每一个特定的需求,并在数据设计中考虑它。如果操作导致矛盾的要求,甚至是不可能的。
    struct OperationTwo : Visitor
    {
        void visit(C&) override { std::cout << "I am C" << std::endl; }
    }
    
    int main()
    {
        A a; B b; C c; D d; E e;
        vector< reference_wrapper< A > > vec = { a, b, c, d, e };
    
        OperationTwo operation_two;
    
        for ( A& element : vec ) 
        {
            element.accept( operation_two );
        }
    }
    
    struct IAmAC
    {
        void operator()( C& ) { std::cout << "I am C" << std::endl; }
        void operator()( A& ) {} // Fallback
    };
    
    using OperationTwo = OverloadVisitor<IAmAC>;