面向对象编程:哪个类应该拥有一个方法?

34
我不太理解类与方法之间的关系。方法是对象所执行的操作,还是针对对象执行的操作?或者这是完全不同的概念吗?
具体来说,在图书馆软件系统中,borrow() 方法应该属于代表图书馆用户的类,还是代表借阅物品的类?我的直觉是应该像英语句子结构一样写成 patron.borrow(copy) 的形式,即主语动词宾语;但是我的指导教师说这是错误的,我不明白为什么他认为 borrow() 应该属于 Copy 类(而且他没有很好地解���清楚)。我不需要证明正确性,只是想请有人解释一下正确的关系是什么。
编辑:这个问题被关闭为“离题”。我不明白,难道软件设计问题不适合在这个网站上讨论吗?

3
他希望它是 library.loan(patron, copy) 吗?可能有很多可行的设计方案。如果您不理解,为什么不问呢?请注意,我没有进行任何解释或添加额外的内容。 - Carl Norum
Frungi这是个概念,所以完全取决于您如何关联类。 - Shaikh Farooque
不,他想要的是 copy.borrowCopy(account)。而且我已经说过他解释得不太好。 - Frungi
2
这一切都是随意的,这不是OOP本身的产物,只是在倡导他对于“正确的”OOP实现方式的看法。客观来说,并没有所谓的“正确方式”,只有被某些人持有的观点。正如@6502所指出的那样,OOP并不是一组高级规则,你所说的是面向对象设计,这与OOP是不同的概念。 - Kristopher Micinski
@KristopherMicinski 你说得对,抱歉,这是针对一个OOD课程的。我把OOD和OOP混淆了。 - Frungi
在C++的情况下,使用自由函数。 - user1203803
11个回答

12

有点主观,但老实说,我会选择信息专家模式,然后做出这样的建议:

library.lend(item, patron)

该库包含其所拥有物品的信息(可能在其目录中)。 该图书馆将物品借给读者(它知道读者是谁,因为它会登记他们)。

不确定您的教师如何看待此问题,但这是适合您场景的“抽象”级别(软件对象模仿现实世界实体)。


2
这可能会使库类处理太多可以在其他类中完成的东西。 - Ray Cheng
@RayCheng 是的,但这种方法似乎比我自己的方式更合理,我的方式是通过patron方法在书上设置属性,而我的导师的方式也不如此。 - Frungi
@RayCheng:可以这么说,但这是面向对象的领域。我更喜欢将项目和用户之间的“基于库”的交互封装在图书馆中(中介者模式-只是为了提一下 :))。 - Ryan Fernandes
@RayCheng:也许这个库可以由处理不同“其他事情”的其他对象组成(虽然不确定实际上如何工作)。 - George Duckett

10

不要把OOP的概念与Java或C++等特定实现混淆。

"方法是对象的属性"这个限制并不是OOP的一部分,而只是某些实现的特点。正如你所发现的,它并不适用于大规模应用。

"整数"对象应该有多少方法?哪种更合理:myfile.write(myint)还是myint.write(myfile)?事实上,并没有一个好的普遍答案。将“方法是单个对象的一部分”作为一种特例并不总是适用的,有时为了使问题适应这个解决方案需要做出明显的弯曲,甚至可能接近停滞状态。当一个方法除了正在处理的对象之外没有其他参数时,单派发才是完美的答案:只有涉及单个类型时,单派发才是真正可接受的答案。

在其他语言中,对象和方法是分开的,例如,您可以有文件对象、整数对象和write(myfile, myint)方法描述需要执行的操作…而此方法既不属于文件也不属于整数。


2
你是唯一提到调度的人。也许你应该详细说明为什么在决定放置方法时这很重要?这对于原帖作者可能会有所帮助。 - Luc Danton

7

首先,一些常见的词语。

软件构建不应该由英语语言规则或“美感”之类的东西来支配,它是一门工程学科。考虑你的设计是否解决了问题,是否易于维护,是否易于测试,是否可以并行开发等等。如果你想要更加正式的规范,请查看 D. L. Parnas 的《将系统分解为模块应使用的标准》。

至于你的图书馆例子。想象一下,如果你有一个在图书馆外的副本,那么它应该有“借出”方法吗?借阅如何注册?你是否同意 Copy 或 Patron 类负责数据存储?把 “borrow” 方法放到 Library 类中更加合适。职责将被清晰地划分,你不需要知道太多关于借阅的细节来实现 Copy 和 Patron,也不需要太多关于它们的细节来实现 Library。


3
正如@Ryan Fernandes所说,借出/借入操作不能与Patron或Book关联。它必须与一个知道图书馆所有书籍和读者状态的类相关。例如,书籍是否有待处理的预订?有多少本可用副本?这位读者是否已缴纳所有费用?他是否有资格借取这本书?因此,通常应该在Library或LibraryService类中实现此功能。

3

类中公开的方法是可以在实体上执行的任务。这样,类只会封装其行为。

例如:

Computer.TurnOn()

该方法仅适用于计算机系统。

相反,如果我说:

SomeOne.TurnonComputer()

现在,某人将有责任打开计算机(设置计算机相关属性),这意味着我们没有满足封装的概念,而是将类的属性散布到各个地方。


3
OOP的核心是创建多态函数,在每个实现中处理一组遵守特定不变量的数据。
因此,改变对象状态的方法应该在该对象的类中定义。纯功能代码的位置并不那么重要,但如果它只有一个输入,则应该放在其输入类型上,或者放在其输出上。
在您的示例中,如果borrow更改了copy中的数据,则应将其放置在那里。但是,如果您通过将书籍的借阅状态建模为它被保存在特定集合中(无论是在读者手中还是在图书馆的集合中),则将borrow放在保存该书籍的类上可能更有意义。然而,后一种设计存在一个问题,即副本可能在多个集合中,因此您需要在副本上放置一些信息(以及相应的方法)。

在OP的例子中,我猜测该方法将会改变Patron和Library中的数据。因此,可能应该有两个方法,一个在每个类中:Library.lend(book, patron)Patron.loan(book)lend将会改变Library中必要的数据并调用Patron上的loan,这将会改变Patron中必要的数据。 - KaptajnKold
哈哈,不,我错过了那个。显然我的评论是多余的。 - KaptajnKold

1
方法是对象所做的事情,还是对它执行的操作?或者这是完全不同的概念?首先让我澄清一些关于类和对象的东西。
“类”通常用于表示特定的类别。 像汽车(而不是法拉利或保时捷),水果(而不是香蕉或苹果)等。
因此,是法拉利被驾驶,香蕉被食用。 而不是它们的类别。
一个对象总是具有属性并具有行为。
甚至针对您的情况,"borrow()" 方法是由 "person" 对象在另一个由 "library system" 对象保存记录的 "book" 对象上执行的操作/行为。
对于我来说,以面向对象的方式表示这个问题的好方法是:
libray.borrow(new book('book title'), new person('starx'));

只是出於娛樂,你對這個有什麼看法?

person starx = new person('starx');
book title1 = new book('title1');
library libraryname = new library('libraryname');
libraryname.addBook(title1);

if(starx.request(title1, libraryname)) {
     starx.take(library.lend(title1, starx));
}

我不是在问类与对象的区别,我在问该方法应该属于哪个类。比如说,它是mouth.eat(banana)还是banana.eat() - Frungi
@Frungi,这就是我试图解释的。请查看更新。 - Starx
一个人不应该借书的副本。如果图书馆所有的副本都已经借出去了怎么办?你是抛出异常还是返回bookingId的null值?copy.borrow(new person('startx')); 更好一些。 - Ray Cheng
那我撤回我的抱怨。=) - Frungi
@RayCheng 我想顾客在借阅时会手持副本。 - Frungi
@Frungi,你对这个更新有什么看法? - Starx

1

对于确切的理由我不是很确定,但你可以这样想,如果多个患者去看医生,只有医生知道什么时候叫下一个患者,所以下一个方法应该是医生职责的一部分,尽管认为下一个应该是患者的责任很诱人,因为他必须接下来去看病,但在某些情况下,当图书馆书籍需要借出时,它应该是书籍类型而不是读者的责任,因为书籍(资源)知道何时会空闲。


0

我想这可以两种方式实现。没有硬性规定。重点是逻辑上合理的分组函数。对我来说,Patron#borrow(BookCopy)BookCopy#borrow(Patron) 意义相同。或者你可以有一个类 LibManager.borrow(BookCopy, Patron)


0

你的讲师是对的。嗯,实际上他是错的。我不知道。

我的观点是,对于这样的问题,通常没有确定的一般性答案。它主要取决于在您特定情况下最有效的方法。选择最容易编码的方式-它将是最容易维护的方式。而且,“最容易编码”也建议考虑类的预期用户(不仅仅是您的LibraryCopyPerson类)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接