2010-09-19 62 views
11

我正在尝试编写面向对象风格的程序。编码两个对象之间的交互时,我有一些混淆。面向对象的风格编程,用于对象之间的交互

场景: 人(约翰)给人(贝蒂)$ 5

可能的解决方案(伪代码):

A)John.pays(贝蒂,5); B)Betty.receives(John,5); C)Bank.transfer(John,Betty,5);
D)
开始交易:
John.decrease(5);
Betty.increase(5);
结束交易:
E)Service.transferMoney(John,Betty,5); //服务是一个通用的服务对象

请告诉我哪一种方法更适合于OOP编码方式,以及它背后的原因。我正在寻找一些指导,如“告诉,不要问”规则。

谢谢。

+0

这个问题似乎是题外话题,因为它是关于代码审查,并适合代码review.stackexchange.com – 2014-11-10 04:51:43

回答

0

我的投票:C.其中C做D做的事情(例如不亏钱等)。

在这个小例子中,“银行”是一个完全有效的实体,它知道约翰和贝蒂有多少钱。约翰和贝蒂都不应该对银行撒谎。

不要害怕反转(或不)按照情况所需的“OO”程序中的逻辑。

0

您应该根据您的域进行建模。选项C看起来是最好的选择,因为它将事务逻辑分为Bank \ Service类。

0

这是一个我经常与自己一起作为新手程序员奋斗的问题。我同意“C”似乎是最好的选择。在这样的情况下,我认为最好使用“中性”实体,如“银行”。这实际上模拟了大多数现实生活中重要的交易,因为大多数进口交易使用支票和/或信用(中立的第三方)。

2

我会投票给没有上述:)

约翰为什么支付贝蒂?这是一个重要的问题,因为它解释了切入点的位置。假设约翰欠了贝蒂钱,而且这是发薪日。

public class John 
{ 
    public void onPayday() 
    { 
     Betty.Receive(5.0f); 
    } 
} 

这是,如果你想要一个纯粹的对象交互风格的方法,当然。

这里的区别在于我们没有一个外部例程来协调John和Betty之间的交互。相反,我们让约翰回应外部事件,并选择何时与贝蒂进行互动。这种风格也可以很容易地描述所需的功能 - 例如“在发薪日,约翰应该支付贝蒂。”

这是一个非常好的控制反转意味着什么的例子 - 对象互相交互,而不是被某些外部例程操纵。这也是Tell,Do not Ask的一个例子,因为对象是告诉彼此的事情(约翰被告知这是发薪日,约翰告诉贝蒂接受5美元)。

+0

_ @ kyoryu_:这听起来非常好,但实际的资金转移*发生在哪里*,以及那代码是什么样的?正如我理解你的答案,“Betty.Receive(5.0f)”实际上并没有转移资金,而只是触发了一个事件(“有人问贝蒂收到5美元”)。否则(如果该方法调用确实进行汇款),那么您肯定会投票选择OP的选项B? – stakx 2010-09-19 08:07:27

+0

@stakx:这是一个显示对象交互的简单示例。这个选项和选项B之间的主要区别在于,在选项B中,操纵约翰和贝蒂还有一个更高层次的过程,并且充当中介。在这个选项中,John和Betty直接*互动,根本不需要中间人。真正的代码还包括约翰扣除他的5美元,并且当贝蒂成功收到它时承诺或回滚 - 类似于这里描述的情况:http://www.eaipatterns.com/ramblings/18_starbucks.html – kyoryu 2010-09-19 17:22:03

2

这里有许多备用解决方案。例如,

Betty.Receieves(John.Gives(5))

这假设给出函数返回给定的量。

tx = CashTransaction(John, Betty); 
tx.Transfer(5); 

这假设第一prameter是付款人,第二个是收款人,那么你就可以在不产生新的对象进行多笔交易。

事情可以建立在许多方面。你应该选择最接近你想要建模的那个。

+0

这将使Gives()方法在Receives()方法()上高度耦合。 – janetsmith 2010-09-19 15:45:56

+0

不,它不。只给回报价值,约翰不关心他给谁。贝蒂同样只收到一个数字......并不关心谁给了她什么。 – 2010-09-19 16:06:58

+0

其实,Gives/Receives方法是非常耦合的,它有点泄漏。你可以传递任何数字给它,如果他们没有被“接收”,数字可能会丢失。在现实生活中,你可以通过抽象类,多态性或接口来解决这个问题。然而,在这样简单的结构中,人们必须假定安全性和健壮性不成问题。 – 2010-09-19 16:15:05

1

如果你真的想要得到OOPy,尝试OOP以下

Person  Betty,John; 
CashTransfer PocketMoney; 
PocketMoney.from = John; 
PocketMoney.to  = Betty; 
PocketMoney.amount = 20.00; 
PocketMoney.transfer(); 

点是不是让代码更喜欢书面语言,但有不同的方法和参数,使代码更对象可读。

所以从上面的代码,你可以看到约翰给贝蒂20美元的零用钱。代码是有意义的,允许更容易的代码可读性以及可理解性。

+1

这不是OOPy。这是混淆的。特别是在建造后的不可用的不死状态中有一个对象,坦率地说,是邪恶的。对象应该在可用状态下出生,而不是处于死亡状态。 – 2010-09-19 05:25:22

+1

但是,人们可以有Person :: AddFunds()等方法,这个例子是非常原始的。通过拥有一个单独的对象来传递现金,你赋予传递的意义,以及赋予传递能力被使用无数次,PocketMoney.transfer()将从此使约翰给贝蒂20美元。 – 2010-09-19 05:34:13

+0

你错过了我的观点。您正在实例化CashTransfer(PocketMoney)。那个实例化的类是不死的。你必须用John和Betty的大脑和一些钱来填充它,然后才能真正调用'transfer()'方法。约翰和贝蒂应该在建设者,国际海事组织,数额应该在'调用'调用。 – 2010-09-19 08:52:18

10

我注意到的一件事是,OOP的新手遇到困难,试图将物理世界映射到他们正在编写的代码中。你真的关心约翰和贝蒂是人还是你真的想描绘一个银行账户?我认为,您在示例中选择的对象实际上使得找出问题的解决方案变得更加困难。

这个的重要部分是 1)在哪里把如何移动钱的逻辑。 2)在哪里存储每个人有多少钱的数据。

您需要决定是否要在银行的人员或客户(可能是个人,公司或其他人)的背景下讨论问题。我猜你是在谈论一个顾客,因为假设他是一个人会造成限制和误导。另外,银行是一个非常通用的术语,它是内部人的大型砖结构建筑,还是具有多个不同页面的在线网站,可以完成不同的事情。 一个银行账户对象可以有一个方法(可能是静态的,取决于你决定如何存储你的数据,以及你将如何使用你的对象)知道如何从一个账户转移到另一个账户。如何转移的逻辑不属于贝蒂或约翰或银行,它属于银行帐户,如果涉及费用等,它可以根据帐户类型具有特殊的逻辑。如果你将这一逻辑交给银行,那么最终你会得到一个巨大的银行类,其中包括从客户的大量增加到以非常具体的账户类型处理资金的各种方法。我的每个账户类型都有不同的处理转账的规则。想想您可能想要将转账或存款显示为待处理的时间。

如果您只是解决转账问题,则无需创建一堆对象。根据已知的要求和推测的未来要求,以下将是一个不错的选择。 CheckingAccount.Transfer(johnsAccountNo,bettysAccountNo,金额)

+0

+1,因为在银行账户后面可能会有一家公司,而不是一个人。 – chelmertz 2010-09-19 07:47:54

+2

既然面向对象的创立思想是对现实世界进行建模的能力,所以试图对现实世界进行建模的人已经将这一部分弄对了。让设计从用户心智模型开始也是一个普遍教授的想法(也是MVC基础的一部分) – 2014-11-15 08:31:45

+1

@runefs是的我同意。我的意思是说,你可以让自己陷入一个让你非常容易的状况。 – 2014-11-15 14:42:55

3

我现在可以问一个问题吗?谁控制这笔钱?约翰是否决定交易金额,贝蒂还是一些未指定的第三方?

我问的原因是因为这里没有真正的正确或错误的答案,只是一个可能更灵活或比其他人更强大。如果这是现实生活中的情况,那么我会将交易模型化为交易双方在交易之前必须达成的协议,以及花钱(约翰)开始交易的人。像答案Ç和@Mysterie人

tx transaction_request = John.WantsToBuyFor(5); //check if John can 
if(Betty.AgreesWith(transaction_request))  //check if Betty wants 
{ 
    transaction_request.FinalizeWith(Betty);  //Do it with Betty 
} 

FinalizeWith功能的东西确实数学

void FinalizeWith(Person party) 
{ 
    requestor.cash -= amount; 
    party.cash += amount; 
{ 

你可能想添加一些说明。当然什么项目是约翰购买。

+2

+ 1中看到的内容,而不是具体的代码示例,但是由于思路(控制器和两方必须在控制器完成其工作之前必须达成一致)。对于“与Betty一起做”的交易:) :) – stakx 2010-09-19 08:15:10

3

这个问题的答案是一个漫长而复杂的问题,你会从大量的人那里得到零碎的东西。为什么只有零碎的东西?因为正确答案几乎完全取决于系统的要求。

但是,您必须确保不陷入的一个陷阱是this one。阅读你在这里得到的答案。你会得到很多好的建议。 (最重要的是已被投票的建议。)一旦你已经阅读并理解了这些,read Steve Yegge's rant(并理解它!)。从长远来看,这会让你保持清醒。

0

作为新OOP和最终使用一些OOP,我会说,它应该是A和B.

我们的重点是人,它是由每个人来处理他的钱。我们不知道他是否会使用银行,或者他是否直接从贝蒂获得现金。

您创建了一个Person类,并使用两种方法向该类添加方法:send和recieve。它还必须有一个名为余额的公共变量来跟踪它们的余额。

您创建两个Person对象:Betty和John。相应地使用方法。像John.sends一样(Betty,5)。这应该创造贝蒂并更新贝蒂的平衡。

如果他们想使用银行该怎么办?添加另一种方法,比如说......传输(acct),不管它是什么。

这就是我想的。

2

有一种纯粹的面向对象的属性可以帮助轻松通过雷达下的例子,但object-capability模型使明确和中心。链接文档(“从对象到功能”(FOtC))详细讨论了该主题,但是(简而言之)能力的一点是,对象影响其世界的能力仅限于它所引用的对象。开始时可能看起来并不重要,但在保护访问权限方面非常重要,并影响其他类的方法中可用的类的方法。

选项A)给帐户John访问帐户Betty,而选项B)给Betty访问帐户John;既不可取。使用选项C),账户访问由银行调解,因此只有银行才能窃取或伪造资金。选项D)与其他三种不同:其他显示消息正在发送但不是实现,而D)是一种方法实现,它不显示它处理的消息,也不显示它处理它的类。 D)可以很容易地实施前三个选项中的任何一个。

FOTC有包括一些其他类的解决方案的开头:

  • 封口& unsealers,
  • 皮包,这是有点像,它们包含基金账户,但不一定有一个所有者。
  • 薄荷糖,这是可以与正余额

甲薄荷具有缩放器/​​ unsealer对,它赋予到钱包每当薄荷创建一个创建钱包的唯一的东西。钱包监督平衡变化;他们在减少余额时使用封口机,并且将售货机从一个钱包转移到另一个钱包。钱包可以产生空钱包。由于使用密封剂密封剂,一个钱包只能与由同一薄荷创建的其他钱包一起使用。有人不能自己写钱包来伪钞;只有能够获得薄荷的物体才能创造收益。通过限制访问薄荷糖来防止伪造。

任何有钱的人都可以通过产生一个空钱包并将钱从第一个钱包转入其中来启动交易。然后可以将临时钱包发送给收款人,收款人可以将钱从临时钱包转移到其拥有的其他钱包。通过限制对钱包的访问来防止盗窃。例如,银行代表客户在账户中持有钱包。由于银行只能访问其客户账户和临时钱包的钱包,因此只有客户的银行可以从客户那里窃取(虽然请注意,在银行账户之间的转账中,有两个客户可能成为受害者,因此有两个潜在客户盗贼)。

该系统缺少一些重要的细节,例如货币当局(持有一个或多个造币厂的参考)来创造金钱。

总而言之,货币交易安全地执行起来很棘手,因此可能不是学习OOP基础知识的最好例子。

+1

对第2段中的四个选项+1的很好的解释。其余的答案可能更多地集中在安全性上,但这是一个非常有趣的阅读。 – stakx 2010-09-19 09:38:14