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+中的长委托链+;_C++_Design Patterns_Delegation_Chaining_Method Chaining - Fatal编程技术网

C++ C+中的长委托链+;

C++ C+中的长委托链+;,c++,design-patterns,delegation,chaining,method-chaining,C++,Design Patterns,Delegation,Chaining,Method Chaining,这绝对是主观的,但我想尽量避免 变得爱争论。我认为这可能是一个有趣的问题,如果 人们对待它很恰当 在我最近的几个项目中,我曾经实现过一些体系结构,在这些体系结构中,长委托链是常见的事情 经常会遇到双重委托链: bool Exists = Env->FileSystem->FileExists( "foo.txt" ); 三重授权并不罕见: Env->Renderer->GetCanvas()->TextStr( ... ); 存在高阶的委托链,但实际上很少 在上

这绝对是主观的,但我想尽量避免 变得爱争论。我认为这可能是一个有趣的问题,如果 人们对待它很恰当

在我最近的几个项目中,我曾经实现过一些体系结构,在这些体系结构中,长委托链是常见的事情

经常会遇到双重委托链:

bool Exists = Env->FileSystem->FileExists( "foo.txt" );
三重授权并不罕见:

Env->Renderer->GetCanvas()->TextStr( ... );
存在高阶的委托链,但实际上很少

在上面提到的示例中,没有执行空运行时检查,因为所使用的对象始终存在,并且对程序的功能和性能至关重要 在执行开始时显式构造。基本上,在这些情况下,我曾经拆分一个委派链:

1)我重用通过委托链获得的对象:

{ // make C invisible to the parent scope
   clCanvas* C = Env->Renderer->GetCanvas();
   C->TextStr( ... );
   C->TextStr( ... );
   C->TextStr( ... );
}

<强> 2)<强> >在使用之前应检查委托链中间某个中间对象是否为空。例如

clCanvas* C = Env->Renderer->GetCanvas();

if ( C ) C->TextStr( ... );
我曾经通过提供代理对象来解决这个问题(2),这样就可以在非空对象上调用一个方法,从而得到一个
empty
结果

我的问题是:

  • 情况(1)或(2)是模式还是反模式
  • 在C++中,有一种更好的处理长委托链的方法吗? 以下是我在做出选择时考虑的一些利弊:

    优点:

    • 它非常具有描述性:从1行代码中可以清楚地看出对象来自何处
    • 长链看起来不错
    缺点:

    • 由于很难检查委托链中的多个临时对象,因此交互式调试非常困难

    我想知道长授权链的其他利弊。请给出你的理由,并根据你的观点是否有说服力而不是你是否同意它来投票。

    根据我的经验,这样的链条通常包含不那么琐碎的获取者,导致效率低下。我认为(1)是一个合理的方法。使用代理对象似乎有些过分。我宁愿看到空指针崩溃,也不愿意使用代理对象。

    我不愿意调用任何一种反模式。然而,第一个缺点是,变量
    C
    即使在逻辑上相关之后仍然可见(范围太小)

    您可以使用以下语法绕过此问题:

    if (clCanvas* C = Env->Renderer->GetCanvas()) {
      C->TextStr( ... );
      /* some more things with C */
    }
    

    C++中允许的(虽然它不是C),并允许您保持适当的范围(<代码> c<代码>,如它位于条件块的范围内),并检查NULL。< /P> 断言某些东西不是空的绝对比被错误杀死要好。所以我不建议跳过这些检查,除非您100%确定指针永远不能为空


    此外,如果您觉得特别漂亮,您可以将支票封装在一个额外的免费函数中:

    template <typename T>
    T notNULL(T value) {
      assert(value);
      return value;
    }
    
    // e.g.
    notNULL(notNULL(Env)->Renderer->GetCanvas())->TextStr();
    
    模板
    T notNULL(T值){
    断言(价值);
    返回值;
    }
    //例如。
    notNULL(notNULL(Env)->渲染器->GetCanvas())->TextStr();
    
    有趣的问题,我认为这是可以解释的,但是:

    我的两分钱

    设计模式只是针对常见问题的可重用解决方案,这些问题具有足够的通用性,可以广泛应用于面向对象(通常是)编程中。许多常见模式将从接口、继承链和/或包含关系开始,这些关系将导致您在某种程度上使用链接来调用事物。这些模式并没有试图解决这样的编程问题——链接只是它们解决手头的函数问题的副作用。所以,我不会真的认为它是一种模式。< /P> 同样,反模式是(在我看来)与设计模式的目的背道而驰的方法。例如,设计模式都是关于代码的结构和适应性的。人们认为单体是一种反模式,因为它通常(不总是)导致蜘蛛网代码,因为它固有地创建了一个全局,当你有很多时,你的设计快速恶化。

    因此,再次强调,链接问题并不一定表明设计的好坏——它与模式的功能目标或反模式的缺点无关。有些设计只有很多嵌套对象,即使设计得很好


    该怎么办:

    长的委托链肯定会在一段时间后成为一个麻烦,只要您的设计规定这些链中的指针不会被重新分配,我认为保存一个指向您感兴趣的链中的点的临时指针是完全正确的(函数范围或更少)

    但就我个人而言,我反对将指向链某一部分的永久指针保存为类成员,因为我已经看到,人们最终会有30个指向永久存储的子对象的指针,而你会完全不知道这些对象在你正在使用的模式或体系结构中是如何布局的

    还有一个想法——我不确定我是否喜欢这个,但我看到一些人创建了一个私有(为了你的理智)函数来导航链,这样你就可以回忆起它,而不必处理指针是否在封面下更改,或者是否有空值的问题。将所有逻辑打包一次,在函数顶部添加一条注释,说明它从链的哪个部分获取指针,然后直接在代码中使用函数结果,而不是每次都使用委托链,这会很好

    性能

    我最后要指出的是,这种函数封装方法以及您的委托链方法都存在性能缺陷。保存一个临时指针可以避免多次额外的两次取消引用
    clCanvas & C = Env.Renderer().GetCanvas();
    
    if ( Env.HasRenderer() ) clCanvas* C = Env.Renderer().GetCanvas();
    
    Environment* env = GetEnv();
    FileSystem* fs = env->FileSystem;
    bool exists = fs->FileExists( "foo.txt" );