关于编码风格:使用return语句效果很好!

Using return statements to great effect!

当我使用返回值创建方法时,我通常会尝试设置一些方法,这样在调用方法时就不会出现这样的情况:它必须返回一些默认值。当我开始的时候,我经常写一些方法来做一些事情,或者返回它们所做的事情,或者,如果它们没有做任何事情,则返回空值。但我讨厌在我的代码中有难看的if(!null)语句,

我正在读一本关于Ruby的指南,我在很多月前读过,是由务实的程序员编写的,我注意到他们经常在通常不会返回任何东西的时候返回self(Ruby的this)。他们说,这是为了能够链接方法调用,就像在本例中,使用返回其设置属性的对象的setter。

1
tree.setColor(green).setDecor(gaudy).setPractical(false)

起初我觉得这种东西很吸引人。有几次,我很高兴能够像Player.getHand().getSize()那样链接方法调用,但这与方法调用的对象从一步到另一步有所不同。

堆栈溢出对返回值有什么看法?当你想到回报价值时,是否有什么模式或习语会让你热血沸腾?有没有什么好方法可以避免挫折,增加美感?


在我看来,您应该考虑三种退货情况:

对象属性操作

首先是对对象属性的操作。您在这里描述的模式在操作对象时非常常用。一个非常典型的场景是将它与工厂一起使用。考虑一下这个假设的创建调用:

1
2
3
4
5
// When the object has manipulative methods:
Pizza p = PizzaFactory().create().addAnchovies().addTomatoes();
// When the factory has manipulative methods working on the
// object, IMHO more elegant from a semantic point of view:
Pizza p = PizzaFactory().create().addAnchovies().addTomatoes().getPizza();

它允许快速掌握正在创建的内容或对象是如何操作的,因为这些方法形成了一个人类可读的表达式。当然不错,但不要过度使用。经验法则是,这可能用于返回值也可以声明为void的方法。

正在评估对象属性

第二种可能是当一个方法在一个对象上计算某个值时。例如,考虑方法car.getCurrentSpeed(),它可以被解释为向对象发送一条消息,请求当前速度并返回该速度。它只返回值,不太复杂。:)

使对象做这个或那个

第三种可能是当一个方法执行一个操作时,返回某种类型的值,指示调用者的意图得到了多大程度的满足——但是,列出这样的方法可能很困难:

1
2
int new_gear = 20;
if (car.gears.changeGear(new_gear)) // does that mean success or fail?

在这里,您可以看到设计方法的困难。成功或失败时应该返回0吗?如果因为汽车只有5个档位而无法设置档位-1怎么样?这是否意味着现在的档位也是-1?方法可以返回它更改的档位,这意味着您必须将提供给方法的参数与返回代码进行比较。那就行了。另一方面,您可以简单地返回"真"或"假"作为失败,或者返回"假"或"真"作为失败。可以通过估计这些方法调用是否会失败或成功来决定要使用哪个方法。

在我看来,有一种方法可以通过对返回值进行语义描述来更好地表达这些返回值的语义。未来的开发人员与您的对象交互时会喜欢您不必为您的方法查找注释或文档:

1
2
3
4
5
6
7
8
class GearSystem {
// (...)
public:
enum GearChangeResult
{ GearChangeSuccess, NonExistingGear, MechanicalGearProblem };

GearChangeResult changeGear (int gear);
};

这样,对于任何查看您的代码的程序员来说,返回值的含义都会变得非常明显(考虑:if (gears.changeGear(20) == GearSystem::GearChangeSuccess)—比上面的示例更清楚这意味着什么)。

反模式:作为返回代码的失败。

我实际上忽略了返回值的第四种可能性,因为在我看来,它不是任何:当程序中出现错误时,比如逻辑错误或需要处理的故障——理论上,您可以返回一个指示这样的值。但今天,这种情况不再经常发生(或者不应该发生),因为对于这种情况,也有例外。


返回this也用于"构建器模式",另一种情况是方法链接可以提高可读性和编写方便性。

空值通常作为带外值返回,以指示无法生成任何结果。我认为在没有结果的情况下,这是完全合理的;例如,在文件结尾处从readLine()返回空值,或者在为Mapget(...)方法提供不存在的密钥时返回空值。读取到文件结尾是正常行为(与IOException相反,IOException表示尝试读取时出现异常错误)。同样,查找一个键并被告知它没有值是正常情况。

对于某些情况,空的一个好的替代方法是"空对象",它是结果类的完整实例,但对于"无人之家"的情况,它具有适当的状态和行为。例如,查找不存在的用户ID的结果很可能是一个空用户对象,该对象具有零长度的名称,并且没有在系统中执行任何操作的权限。


我不同意方法不应该返回空值。最明显的例子来自系统编程。例如,如果有人要求打开一个文件,如果打开失败,您只需给他们空值。没有明智的选择。在其他情况下,当在链接列表的最后一个节点上调用时,空值是合适的,例如getNextNode(node)方法。所以我猜这些情况的共同点是,空代表"无对象"(没有文件句柄或没有列表节点),这是有意义的。

在其他情况下,该方法不应该失败,并且有一个适当的异常工具。然后,我认为像您的例子一样的方法链接可以产生很大的效果。我觉得你似乎认为这是"务实的程序员"的一项创新有点可笑。事实上,它可以追溯到口齿不清,如果不是以前。


我很困惑。OO编程语言需要smalltalk的分号:

1
2
3
tree color: green;
     decor: gaudy;
     practical: false.

对象方法1;方法2。表示"对obj调用method1,然后对obj调用method2"。这种对象设置非常常见。


通常,逻辑上不返回任何东西的方法应该是什么都不返回-C++"无效"。对于没有这种选择的语言,不必麻烦返回任何内容。故障应以异常指示。""搜索"方法是边界线-在那些方法中,有时在找不到您要查找的内容时返回"特殊"值很有用。

smalltalk还有另一个有用的习语:"ifnotfound"值或块。