首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么foo->bar->foobar被认为是糟糕的风格?如何在不添加代码的情况下避免呢?

为什么foo->bar->foobar被认为是糟糕的风格?如何在不添加代码的情况下避免呢?
EN

Stack Overflow用户
提问于 2012-07-10 21:05:07
回答 5查看 1.3K关注 0票数 16

我们的C++教授提到,使用operator->的结果作为另一个operator->的输入被认为是不好的风格。

因此,与其写:

代码语言:javascript
复制
return edge->terminal->outgoing_edges[0];

他更喜欢:

代码语言:javascript
复制
Node* terminal = edge->terminal;
return terminal->outgoing_edges[0];

  1. 为什么这被认为是糟糕的风格?
  2. 我如何重构我的程序,以避免‘糟糕的风格’,同时又避免根据上述建议创建的额外代码行?
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-07-10 21:15:08

你应该问问你的教授,为什么他认为这是一种糟糕的风格。我不这么认为,但是我认为他在终端声明中省略const是一种糟糕的风格。

对于像这样的单个代码片段来说,它的风格可能还不错。但是,请考虑以下内容:

代码语言:javascript
复制
void somefunc(Edge *edge)
{
   if (edge->terminal->outgoing_edges.size() > 5)
   {
        edge->terminal->outgoing_edges.rezize(10);
        edge->terminal->counter = 5;
   }
   ++edge->terminal->another_value;
}

这开始变得笨拙起来--它很难读,也很难写(我在输入的时候犯了大约10个打字错误)。它需要对这两个类的运算符->进行大量计算。如果运算符是微不足道的,但如果运算符做了任何令人兴奋的事情,那么它最终将做大量的工作。

所以有两个可能的答案:

  1. Maintanability
  2. Efficiency

而像这样的单个代码片段,您无法避免额外的行。在上面这样的情况下,它将导致更少的输入。

票数 12
EN

Stack Overflow用户

发布于 2012-07-10 21:11:43

原因有很多。

Law of Demeter给出了一个结构原因(请注意,您的C++教授的代码仍然违反了这一点!)。在您的示例中,edge必须了解terminaloutgoing_edges。这使得它紧密耦合在一起。

作为替代方案

代码语言:javascript
复制
class Edge {
 private:
   Node* terminal;
 public:
   Edges* outgoing_edges() {
      return terminal->outgoing_edges;
   }
}

现在,您可以在一个地方更改outgoing_edges的实现,而不必到处更改。老实说,对于像图这样的数据结构(它是紧密耦合的,边和节点不能彼此逃脱),我并不真的相信这一点。在我的书中,这将是过于抽象的。

还有空引用的问题,在表达式a->b->c中,如果b为空怎么办?

票数 19
EN

Stack Overflow用户

发布于 2012-07-10 21:17:16

嗯,我不能肯定你的教授的意思,但我有一些想法。

首先,像这样的代码可能是“不那么明显的”:

代码语言:javascript
复制
someObjectPointer->foo()->bar()->foobar()

你不能真正说出foo()的结果是什么,因此你也不能真正说出bar()被调用的是什么。这是同一个物体吗?或者它可能是一个正在创建的临时对象?或者可能是别的原因?

另一件事是如果你必须重复你的代码。考虑下面这样的例子:

代码语言:javascript
复制
complexObject.somePart.someSubObject.sections[i].doSomething();
complexObject.somePart.someSubObject.sections[i].doSomethingElse();
complexObject.somePart.someSubObject.sections[i].doSomethingAgain();
complexObject.somePart.someSubObject.sections[i].andAgain();

将其与以下内容进行比较:

代码语言:javascript
复制
section * sectionPtr = complexObject.somePart.someSubObject.sections[i];
sectionPtr->doSomething();
sectionPtr->doSomethingElse();
sectionPtr->doSomethingAgain();
sectionPtr->andAgain();

您可以看到第二个示例不仅更短,而且更容易阅读。

有时函数链更简单,因为您不需要关心它们返回的内容:

代码语言:javascript
复制
resultClass res = foo()->bar()->foobar()

vs

代码语言:javascript
复制
classA * a = foo();
classB * b = a->bar();
classC * c = b->foobar();

另一件事是调试。调试长的函数调用链是非常困难的,因为您根本不能理解它们中的哪一个会导致错误。在这种情况下,打破链条很有帮助,更不用说你还可以做一些额外的检查,比如

代码语言:javascript
复制
classA * a = foo();
classB * b;
classC * c;
if(a)
   b = a->bar();
if(b)
   c = b->foobar();

所以,总的来说,你不能说它是一种“坏”或“好”的风格。你必须先考虑你的情况。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11413939

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档