将标志传递给方法的替代方法?

Leo*_*Hat 23 c++ parameters inheritance

有时在修复现有代码库中的缺陷时,我可能(通常是出于懒惰)决定更改方法:

void
MyClass::foo(uint32_t aBar)
{
    // Do something with aBar...
}
Run Code Online (Sandbox Code Playgroud)

至:

void
MyClass::foo(uint32_t aBar, bool aSomeCondition)
{
    if (aSomeCondition)
    {
        // Do something with aBar...
    }
}
Run Code Online (Sandbox Code Playgroud)

在代码审查期间,一位同事提到了一种更好的方法是子类MyClass来提供这种专门的功能.

但是,我认为,只要aSomeCondition不违反其目的或凝聚力,MyClass就可以使用它.只有当代码被标志和if语句渗透时,继承才是更好的选择,否则我们可能会进入架构宇航员领域.

这里的转折点是什么?

注意:我刚看到这个相关的答案表明a enum可能是比a更好的选择bool,但我认为我的问题仍然适用于这种情况.

VGE*_*VGE 28

这种问题不仅有一种解决方案.

Boolean的语义非常低.如果你想在将来添加一个新的条件,你将不得不添加一个新的参数...
经过四年的维护,你的方法可能有六个参数,如果这些参数都是布尔值,它是维护者的非常好的陷阱.

如果案件是独家的,Enum是一个不错的选择.枚举可以轻松迁移到位掩码或上下文对象.

位掩码:C++包含C语言,你可以使用一些简单的旧实践.有时无符号int上的一个掩码是一个不错的选择(但你放松了类型检查),你可以错误地传递一个错误的掩码.这是从布尔或枚举参数平滑移动到这种模式的便捷方式.可以通过一些努力将位掩码迁移到上下文对象.您可能必须实现某种按位算术,例如operator |, operator &如果您必须保持构建时兼容性.

如果行为的分割很大并且此行为与实例的生命周期相关,那么继承有时是一个很好的选择.请注意,您还必须使用多态,如果大量使用此方法,这可能会降低方法的速度.
最后,继承会引起你所有工厂代码的变化......如果你有多种方法可以独家改变,你会怎么做?你会弄乱你的特定类的代码......事实上,我认为这通常不是一个很好的主意.

方法拆分:另一种解决方案是将方法拆分为多个私有方法并提供两个或多个公共方法.

上下文对象:可以通过添加上下文参数来绕过C++和C缺少命名参数.我经常使用这种模式,特别是当我必须在复杂框架的层次上传递许多数据时.

class Context{
public:
  // usually not a good idea to add public data member but to my opinion this is an exception
  bool setup:1; 
  bool foo:1;
  bool bar:1;
  ...
  Context() : setup(0), foo(0), bar(0) ... {}
};
...    

Context ctx;
ctx.setup = true; ...
MyObj.foo(ctx);
Run Code Online (Sandbox Code Playgroud)

注意:这对于最小化访问(或使用)静态数据或查询单例对象,TLS也很有用...上下文对象可以包含更多与算法相关的缓存数据.......让你的想象力自由发挥......

反模式

我在这里添加了几种反模式(以防止签名的某些变化):*永远不要这样做*

  • *从来没有这样做*使用静态int/bool进行参数传递(有些人这样做,这是删除这种东西的噩梦).打破至少多线程......
  • *从不这样做*添加数据成员以将参数传递给方法.

  • 特别是对于从未做过此列表的+1 (4认同)

小智 20

不幸的是,我不认为这个问题有一个明确的答案(这是我在自己的代码中经常遇到的问题).使用布尔值:

 foo( x, true );  
Run Code Online (Sandbox Code Playgroud)

电话很难理解.

有一个枚举:

 foo( x, UseHigherAccuracy );
Run Code Online (Sandbox Code Playgroud)

它很容易理解,但你最终会得到这样的代码:

 foo( x, something == someval ? UseHigherAccuracy : UseLowerAccuracy );
Run Code Online (Sandbox Code Playgroud)

这几乎没有改进.并具有多种功能:

 if ( something == someval ) {
      AccurateFoo( x );
 }
 else {
      InaccurateFoo( x );
 }
Run Code Online (Sandbox Code Playgroud)

你最终得到了更多的代码.但我想这是最容易阅读的,我会倾向于使用,但我仍然不完全喜欢它:-(

但是我绝对不会做的一件事是子类.继承应该是您可以达到的最后一个工具.

  • "继承应该是你能达到的最后一个工具." 特别是如果这是一个像这里的小功能.+1为其他选项的范围. (7认同)

Pup*_*ppy 10

主要问题是标志是否影响或该函数的行为.函数本地更改应该是参数,而不是子类.运行时继承应该是最后达到的工具之一.


pax*_*blo 5

我使用的一般准则是:如果aSomeCondition主要方式改变函数的性质,那么我考虑子类化。

与添加仅具有较小影响的标志相比,子类化是一项相对较大的工作。

一些例子:

  • 如果它是一个改变排序集合返回给调用者的方向的标志,那是本质上的一个微小变化(标志)。
  • 如果它是一个一次性标志(影响当前调用而不是对对象的持久更改的东西),它可能不应该是一个子类(因为沿着这条轨道可能会导致大量的类)。
  • 如果它是将类的底层数据结构从数组更改为链表或平衡树的枚举,则这是一个复杂的更改(子类)。

当然,通过完全隐藏底层数据结构可能会更好地处理最后一个,但我在这里假设您希望能够选择众多数据结构之一,例如性能等原因。