这是一个好模式吗:用一系列lambda代替长函数?


14

我最近遇到以下情况。

class A{
public:
    void calculate(T inputs);
}

首先,A表示物理世界中的一个对象,这是不将类拆分的有力论据。现在,calculate()事实证明它是一个相当长且复杂的功能。我认为它有三种可能的结构:

  • 写成一堵墙-优势-所有信息都集中在一个地方
  • private在类中编写实用程序函数,并在calculate主体中使用它们-缺点-类的其余部分不了解/不在意/不了解这些方法
  • 编写calculate以下方式:

    void A::calculate(T inputs){    
        auto lambda1 = () [] {};    
        auto lambda2 = () [] {};    
        auto lambda3 = () [] {};
    
        lambda1(inputs.first_logical_chunk);
        lambda2(inputs.second_logical_chunk);
        lambda3(inputs.third_logical_chunk);
    }

可以认为这是好事还是坏事?这种方法有什么问题吗?总而言之,当我再次面临同样的情况时,我是否应该将此视为一种好方法?


编辑:

class A{
    ...
public:
    // Reconfiguration of the algorithm.
    void set_colour(double colour);
    void set_density(double density);
    void set_predelay(unsigned long microseconds);
    void set_reverb_time(double reverb_time, double room_size);
    void set_drywet(double left, double right);
    void set_room_size(double value);;

private:
    // Sub-model objects.
    ...
}

所有这些方法:

  • 获得价值
  • 计算其他一些值,而不使用状态
  • 调用某些“子模型对象”以更改其状态。

事实证明,除了set_room_size()这些方法之外,这些方法只是将请求的值传递给子对象。set_room_size()另一方面,做几个屏幕的模糊公式,然后(2)做一个半屏的调用子对象设置器,以应用各种获得的结果。因此,我将函数分为两个lambda,并在函数末尾调用它们。如果我能够将其拆分为更多的逻辑块,那么我将隔离更多的lambda。

无论如何,当前问题的目标是确定该思维方式是否应持续存在,或者充其量是没有增加价值(可读性,可维护性,可调试性等)。


2
您认为使用lambdas会使您获得函数调用不会带来的好处?
Blrfl 2015年

1
Firstly, A represents an object in the physical world, which is a strong argument for not splitting the class up.当然A表示有关物理世界中可能存在的对象的数据。您可以有一个不含实物的实例,也可以有一个不含实物的实例,因此将它们视为一个且相同是没有意义的。AA
2015年

@Blrfl,封装-没有人calculate()会知道这些子功能。
沃拉克2015年

如果所有这些计算都与just相关A,则将其推到了极点。
Blrfl 2015年

1
“首先,它A代表了物理世界中的一个对象,这是不分裂类的有力论据。” 不幸的是,我在开始编程时被告知这一点。我花了好几年的时间才意识到这是一群曲棍球。这是对事物进行分组的可怕原因。我说不出什么很好的理由组的事情(至少让我满意),但一个是一个,你应该马上放弃。最后,全部是“好的代码”,因为它工作正确,相对易于理解并且相对容易更改(即,更改没有怪异的副作用)。
jpmc26 2015年

Answers:


13

不,这通常不是一个好的模式

您正在做的是使用lambda将功能分解为较小的功能。然而,有一个分手的功能更好的工具:功能。

如您所见,Lambda可以工作,但它们的意义不止是将函数分解为局部位。Lambdas做:

  • 关闭。您可以在lambda的外部作用域中使用变量。这是非常强大且非常复杂的。
  • 重新分配。尽管您的示例使分配工作变得困难,但人们始终必须注意这样一种想法,即代码可以随时交换函数。
  • 一流的功能。您可以将lambda函数传递给另一个函数,这称为“函数式编程”。

在您将lambda混合在一起后,下一个查看代码的开发人员必须立即从脑海中加载所有这些规则,以准备查看代码的工作方式。他们不知道您不会使用所有这些功能。与替代方案相比,这非常昂贵。

就像使用挖土机进行园艺一样。您知道您只是将其用于为今年的花朵开一些小洞,但邻居们会感到紧张。

考虑到您正在做的只是可视地对源代码进行分组。编译器实际上并不关心您使用lambdas进行处理。实际上,我希望优化器在编译时会立即撤消您刚刚所做的一切。您纯粹是在迎合下一个读者(感谢您这样做,即使我们在方法论上意见不一致,代码的读取远比编写的要多!)。您要做的只是分组功能。

  • 在源代码流中本地放置的函数也一样,而无需调用lambda。同样,重要的是读者可以阅读它。
  • 函数顶部的注释说“我们将这个函数分为三个部分”,然后是// ------------------每个部分之间的长行。
  • 您也可以将计算的每个部分放入自己的范围。这样做的好处是立即证明了各部分之间没有变量共享。

编辑:从示例代码中看到您的编辑,我倾向于注释符号是最简洁的,并带有括号来强制注释所建议的边界。但是,如果任何功能可在其他功能中重用,则建议您改用功能

void A::set_room_size(double value)
{
    {
        // Part 1: {description of part 1}
        ...
    }
    // ------------------------
    {
        // Part 2: {description of part 2}
        ...
    }
    // ------------------------
    {
        // Part 3: {description of part 3}
        ...
    }
}

因此,这不是一个客观数字,但是我可以主观地说,lambda函数的存在使我对每一行代码的关注度提高了10倍左右,因为它们有很大的潜力变得危险,复杂,快速等等。天真无邪的代码行(如count++
Cort Ammon 2015年

好点。我认为这些是使用lambda的方法的一些优点-(1)代码在本地相邻且在本地范围内(这将被文件级函数丢失)(2)编译器确保在代码段之间不共享任何本地变量。因此,可以通过calculate()分成{}块并在calculate()范围内声明共享数据来保留这些优势。我认为,看到lambda不会被捕获,读者就不会对lambda的功能感到困惑。
Vorac 2015年

“尽管我看到了lambda未被捕获,但读者不会被lambda的强大功能所困扰。” 这实际上是一个公平但有争议的陈述,触动了语言学的核心。单词的含义通常超出其含义。我的涵义lambda是不公平的,还是您无礼地强迫人们遵循严格的涵义,这不是一个容易回答的问题。实际上,在您的公司使用这种方式可能是完全可以接受的lambda,而在我的公司完全不可接受,而且实际上没有人会错!
Cort Ammon 2015年

我对lambda涵义的看法是基于我在C ++ 03而非C + 11上长大的事实。我花了很多年的时间来理解C ++缺乏功能而受到伤害的特定位置lambda,例如for_each函数。因此,当我看到一个lambda不适合那些容易发现的故障情况时,我得出的第一个假设是它很可能会用于函数式编程,因为否则就不需要它了。对于许多开发人员而言,函数式编程与程序或OO编程完全不同。
Cort Ammon 2015年

感谢您的解释。我是一个新手程序员,现在我很高兴在习惯养成之前就问了我。
Vorac 2015年

20

我认为您做了一个错误的假设:

首先,A代表物理世界中的一个对象,这是不拆分类的有力论据。

我不同意这一点。例如,如果我有一个代表汽车的类别,那么我肯定想将其拆分,因为我当然希望有一个较小的类别来代表轮胎。

您应该将此函数拆分为较小的私有函数。如果确实看起来与类的其他部分分离,则可能表明该类应该分离。当然,没有明确的例子很难说。

在这种情况下,我没有真正看到使用lambda函数的优势,因为它并没有真正使代码更简洁。创建它们是为了辅助功能样式编程,但事实并非如此。

您编写的内容有点类似于Javascript样式的嵌套函数对象。这再次表明他们紧密地在一起。您确定不应该为他们开设单独的课程吗?

总而言之,我认为这不是一个好的模式。

更新

如果看不到将这种功能封装在有意义的类中的任何方法,则可以创建文件范围的帮助器函数,这些函数不是您的类的成员。毕竟这是C ++,不是必须进行OO设计。


听起来很合理,但是我很难想象它的实现。文件范围类具有静态方法?类,在内部定义A(甚至可以与其他所有私有方法一起使用)?在内部声明和定义的类calculate()(这与我的lambda示例非常相似)。为了澄清起见,calculate()是一系列方法(calculate_1(), calculate_2()等)中的一种,它们都很简单,仅此一个就是2个公式的屏幕。
沃拉克2015年

@Vorac:为什么calculate()比其他方法要长得多?
凯文

@Vorac如果没有看到您的代码,真的很难提供帮助。您还可以发布吗?
的Gabor Angyal

@Kevin,所有要求的公式均在要求中给出。代码已发布。
Vorac 2015年

4
如果可以的话,我将投票100次。“在现实世界中建模对象”是对象设计死亡螺旋的开始。在任何代码中,这都是一个巨大的危险信号。
弗雷德神奇狗
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.