OOP原则和方法名称


22
class Boxer:

    def punch(self, punching_bag, strength):
        punching_bag.punch(strength)


class PunchingBag:

    def punch(self, strength):
        print "Punching bag punched with strength", strength

boxer = Boxer()
punching_bag = PunchingBag()

boxer.punch(punching_bag, 2)

毫无疑问,punch对于拳击手来说,这是一个很好的方法名称。但是名字punch对出气筒的方法也好吗?在两种情况下,我的意思是打孔作为命令(即打孔)。

Answers:


23

一个好的经验法则是方法名称应该是动词或谓词,以便您self在其上调用的对象(在标准Python约定中,this在大多数其他语言中)成为主题。

按照这种规则,这file.close是一种错误,除非您采用的是心理模型,即文件会自行关闭,或者file对象并不代表文件本身,而是文件句柄或某种代理对象。

出气筒虽然从不打孔,但punchingBag.punch()任何一种方式都是错误的。be_punched()从技术上讲是正确的,但是很难看。receive_punch()可能起作用,或者handle_punch()。在JavaScript中非常流行的另一种方法是将此类方法调用视为事件,并且约定以事件名称开头(以'on'开头,因此为on_punched()or)on_hit()。或者,您可以采用惯例,即过去分词表示被动语态,按照该惯例,方法名称将为just punched()

要考虑的另一个方面是,出气筒实际上是否知道它受到了什么打击:无论您是打孔,用棍子打它还是用卡车撞上它,都会有所不同吗?如果是这样,有什么区别?您可以将差异归结为一个论点,还是需要不同的方法来接受不同种类的惩罚?具有通用参数的单个方法可能是最优雅的解决方案,因为它使度耦合保持在较低的水平,这种方法不应调用punched()or handle_punch(),而应调用更通用的方法,例如receive_hit()。有了这样的方法,您就可以实现可以击打沙袋的各种角色,而无需更改沙袋本身。


4
@Artur:是和否。文件可以(从概念上来说)在被询问时自行关闭;数组可以自我排序;但是出气筒不会打孔。
tdammers

2
好吧,如果我们的出气筒以疯狂的速度撞在墙上,是出气的墙壁,还是出气筒本身或实际上受到撞击?

1
@tdammers:您的概括建议也可能导致一个名为的接口Hitable
詹斯·皮格萨

2
@Artur:我认为这是OOP认为每个句子都有一个自然主题,并且该想法适用于编程的地方。
tdammers

1
所以主要的问题是。如果文件可以自己关闭,数组可以自己排序等等,为什么出气筒不能自己打孔?有什么真正的区别吗?或者只是在第一种情况下我们已经习惯了,而在第二种情况下我们不习惯了?
风土

6

我认为这是一个概念性问题(我们如何看待世界)。可以说:

  • 看,门正在关上。 door.close()
  • 哇,纸自己折叠了。 paper.fold()
  • 我勒个去?!桌子上的那个文件刚刚关闭,没有人在附近。 file.close()

奇怪的是:

  • 健身房里的那个出气筒刚刚出拳了。 bag.punch()

首先,它需要某种东西来突击自己(例如手臂)。您可能会说:

  • 出气筒已经开始自行移动,就像有人会打它一样。 punching_bag.move()

程序对象可以做其他人通常会做的事情(在“现实世界”中)。但是我想它应该总是至少在某种意义上说事情是在做/自己做的。您应该能够轻松想象它,而不会变得晦涩难懂(例如punching_bag)。


2

我认为这与口味有关。Punching bagpunch()方法至少与自己的行为一致file.close()frame.move()有意义。更大的问题是,为什么Boxer根本没有punch(something)方法?


我喜欢您关于file.close()的观点。那就是我要说的。也许拳击手有拳法,因为还有教练训练拳击手。好吧,事实上,我只是想举一个示例,说明一个动作(消息)通过多个对象传递,而最后一个是“某个动作的对象”。我对list.append(4),account.deposit(50),file.close(),paper.fold()与boxer.punch(),dog.bark(),logger.log()等有轻微的问题。
风土

当通过几个对象时,有两种情况:您使用绑定上下文(自身),而您不使用。如果你这样做,你的方法应该是Coach.sayPunchToBoxer()Boxer.punchNearestBag()Bag.punch()。否则,您必须猜测每次致电都会发生什么Coach.punch()。一般规则是:如果未在方法名称中指定经历动作的对象,则接收者为该对象。

好吧,我认为这也没问题:coach.say_punch(拳击手,打孔袋),boxer.punch(拳击手袋)。即接收者不在方法名称中,而在参数中。
风土

1
当然,我的意思是动作接收者应该可以从调用语句中猜到。

2

您有两种不同的消息:一种命令命令对对象进行打孔,另一种通知对象对它进行打孔。考虑到Boxer对象可能需要对两者都做出响应。不一样。给他们起不同的名字是一个很好的理由。

我的倾向是保留punch(boxer, object, strength)相反的方法并将其重命名为punched。您可以调用它handle_punch或类似的名称,但是无论是处理打孔命令还是被打孔的通知,仍然是模棱两可的。


关于Boxer既需要打孔又需要诸如handle_punch之类的东西(defend在这种情况下将是一个好地方)。但是,沙袋永远不会像这样双向。并且已经有了这个file.close()...
气候

defend是一个命令。一个对象可以响应采取的一种可能的动作punched,但是您不希望其他对象defend直接调用。
user2313838 2013年

2

您的方法最终将导致非常耦合的代码。

理想地总结一下Eric Lippert,您希望拳击手能够做很多事情。将出气筒作为拳击手功能的签名意味着,拳击手是在具有全部(即可打孔)的立即知识的情况下创建的。再加上打孔和打孔是两个非常不同的事情,因此它们不应共享相同的名称。

我宁愿将此模型建模为创建打孔器(包含打孔器的属性力,范围,方向等)的Boxer。

然后,使用带有onPunch之类的方法的出气筒接收该打孔对象可以计算出打孔对其自身的影响。

记住这一点,事物的名称非常重要。它必须适合您所处情况的心理模型。如果您发现自己试图解释发生的事情,乍一看是没有意义的,或者如果您最难以命名的话,那么您的模型可能是错误的,需要更改。

开始后很难更改模型,人们通常会倾向于改变现实以适应模型。这样做的问题是,当您弯曲事物以使其适合(例如可以将事物穿孔的出气筒)时,您所创建的世界变得越来越复杂,而交互也变得越来越难以实现。最终,您将添加甚至最琐碎的内容变成更改和错误的噩梦。即使最初的成本在当时被认为是最便宜的事情,但这种概念性技术债务的价格可能非常高。


1

这就是我称之为“对象/主题”混淆的问题,它非常普遍。

句子通常都有一个主语,在其目标宾语上做动词

现在,关于编程的唯一实际操作是计算机。或实际上是一个过程,线或纤维。默认情况下,对象不设置动画。他们没有自己的线程在运行,因此他们什么也做不了。

这意味着方法对它们起作用,它们是操作的目标,而不是谁执行操作。这就是为什么我们称它们为“对象”而不是“主题”!

当您说File.close不是文件本身关闭时,它是当前正在运行的线程关闭文件。如果您说Array.sort,则当前正在运行的线程对该数组进行排序。如果您说HttpServer.sendRequest,则当前正在运行的线程将请求发送到服务器(反之亦然!)。类似地,这PunchingBag.punch意味着当前的运行线程正在打孔。

这意味着,如果您希望Boxer能够打孔,则它必须是a的子类,Thread以便可以在其线程功能中执行诸如打孔袋的操作。

但是有时在每个对象都有自己的线程的情况下,说出拳是有道理的,您可能想要避免竞争条件并在消息传递时实现方法调用:通过向其发送punch消息来打孔,这就是线程打孔然后,它本身会向您发送punch successful消息,但这只是实现细节。


0

我同意“打孔”是Boxer类的一个很好的方法名称,因为(有些调整)它可以针对其他对象重用。它还准确地描述了一个类的对象正在对另一个对象进行操作。不过,我将方法重命名为“ doPunch”,以更清楚地说明这种关系。

但是,对于PunchingBag类,我发现方法名称过于模糊或对方法中发生的事情不太准确。当我看到“打孔”时,我认为是在打其他孔。但是,此处的PunchingBag对象对来自对象(在这种情况下为Boxer对象)的打孔反应。因此,我将此处的方法重命名为“ isPunched”,以说明对象正在对打孔做出反应。

不过,这是我对如何命名方法的解释。这完全取决于您的口味和遵循的标准。


3
isPunched确实具有误导性(或多或少,取决于框架命名方案)。

通常,将方法应用于在其上被调用的对象。正义到底有什么问题punch()

好吧,我完全理解需要指定操作方向,但是我认为有关OOP及其哲学的某些知识使此操作不必要。与这种著名的解释相关的某种抽象,即对象之间相互“发送消息”。
风土

如果从方法名称中不清楚该方法在做什么,则名称存在问题。OO或使用任何范例都不是问题。这就是在任何要使用它的上下文中,出气筒上的punch()都是错误的原因。当您说出气筒时是什么意思?这也是为什么您无法通过任何哲学假设在假设造成歧义的情况下不需要某些东西的原因。在某些情况下,经验法则会起作用,而在某些情况下则不会。如果经验法则始终有效,则将它们称为规则(没有“经验法则”)。
Dunk

-2

嗯。我在问一个沙袋课,因为您并不真正在意沙袋-您在乎拳击手拳头的冲击力和强度。因此,方法应与衡量和报告冲头影响的方法有关。即使这来自“打孔包”,命名也应显示责任-例如punchImpactMeter等。


-3

拳击手拳打沙袋-> boxer.punch

拳击手将出气筒-> punchingbag.get_punch


3
这似乎并没有提供任何实质性的制作上分和6分之前解释的答案
蚊蚋
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.