关于oop:设计问题:电话会拨打电话号码,还是电话会在电话上自行拨号?

关于oop:设计问题:电话会拨打电话号码,还是电话会在电话上自行拨号?

Design question: does the Phone dial the PhoneNumber, or does the PhoneNumber dial itself on the Phone?

这是从我在DDD Yahoo!上发布的内容重新发布的。 组。

在所有条件都相同的情况下,您是写phone.dial(phoneNumber)还是phoneNumber.dialOn(phone)? 请记住将来可能的要求(除了电话号码之外的帐号,除了电话之外的计算器)。

这种选择倾向于说明信息专家,单一责任原则和告诉不要问的成语如何相互矛盾。

phoneNumber.dialOn(phone)支持信息专家和告诉不要问,而phone.dial(phoneNumber)支持单一责任原则。

如果您熟悉Ken Pugh在Prefactoring中的工作,那么这就是Spreadsheet难题; 您添加行还是列?


phone.dial(),因为是进行拨号的电话。

Actor.Verb(输入)->输出。


Meh-User.Dial(number)。在给定的上下文中,电话毫无意义。 SOL(大声说)是一种很好的方式来思考这一问题(不包括成语和原则):

电话有拨号盘。他们无法拨号。
电话号码是数字。
用户在电话拨号盘上拨打电话号码。


该问题假定答案的上下文,从而造成错误的困境

在此示例中,"电子表格难题"是错误的二分法:行和列是表示层,不一定是数据层。下面的评论告诉我,我误解了这个类比,但我不这么认为-说"这应该是一行还是一列,更可能会更改"是在问题空间上进行不必要的选择-它们都是同样有可能改变。在此特定示例中,这将导致为解决方案选择错误的(是错误的)范例。拨打电话是旧的机械设备如何启动与另一个旧的机械设备的连接。这几乎不是现代电话的恰当比喻。并假设有一个"用户"发起呼叫只是解决了问题-尽管它沿正确的方向(即远离旋转电话型号;-)移动了问题

如果您看一下TAPI的工作原理(很抱歉输入错误,那是TAPI而不是ATAPI!),那么就有一个呼叫控制器-在某种意义上相当于'用户',它可以管理设备之间的连接。一个设备不呼叫另一设备,呼叫控制器连接设备。因此,以下示例在本质上仍然是正确的。使用CallController对象而不是通用Connection可能更正确,但是类比应该足够清晰。

在此示例中,电话是地址也称为"电话号码"的设备。"拨号"操作员在两个设备之间建立连接。因此答案是:

1
2
3
4
5
6
Phone p1 = new Phone(phoneNumber1);
Phone p2 = new Phone(phoneNumber2);
Connection conn = new Connection(p1,p2);
conn.Open();
//...talk
conn.Close();

通过重载Connection以包括设备或其他连接的列表(例如,

1
2
3
4
5
Connection confCall = new Connection(p1,p2,p3,p4,p5,p6);
confCall.Open();

Connection joinCall = new Connection(confCall,p7,p8,conn);
joinCall.Open();

查看TAPI协议以获取更多示例


选择是为列对象还是行对象提供拨号方法,都不会改变程序的缩放比例。

Dial方法本身将只是行和列方法的序列。您必须问这些方法依赖什么。

如果行方法的顺序不取决于确切地知道涉及哪个列对象(而是取决于所涉及的特定行对象),反之亦然,那么按列方法的顺序将问题扩展为m + n(m =数行,n =列数)。创建新行时,如果为列方法分配了"拨号"方法,那么它实际上不会为您节省任何工作。您仍然必须指定行方法的唯一序列,以便在某处的"拨号"中使用!

但是,如果说"拨号"中的列方法序列甚至不取决于所涉及的列对象(它们使用一个"通用"列方法序列),那么问题只会扩展为m。您是否已将"拨号"方法分配给列对象实际上并不重要,程序仍会缩放为m;添加更多的列对象时,基本上不需要做任何工作来制作新的拨号方法,并且您显然可以选择将所有这些拨号方法本身抽象为一个通用的拨号方法。


答:phone.dial(phone_number)

PhoneNumber愚蠢,只是一个数据集。当发生"拨号"时,PhoneNumber对象应该知道如何拨号吗?有许多州需要跟踪,例如:

  • 电话已经在通话吗? (如果是/否,该怎么办?)
  • 如果更改拨号方式会怎样? (全球漫游,其他运营商等)
  • 还有,范围呢?拨打电话时,需要将电话号码添加到最近的去电列表中。

如果您的PhoneNumber对象需要了解所有这些信息,则它不是DRY,并且您的代码会更少
便携式,更容易损坏。

我想说的是史蒂文·A·洛(Steven A.Lowe)。这应该由Controller类型的对象来处理,以处理不同的状态,等等。让PhoneNumber对象保持愚蠢状态,并向需要担心保持电话嗡嗡作响的中间人提供一些技巧。


都不行用户在电话上拨打电话号码。


显然,可以从PhoneUserFactory.CreatePhoneUser()方法获得实现的PhoneUserInterface接口具有可用于拨打电话的方法dial(Phone,Number)。

编辑:回答评论。都不行手机应该有一个buttonPressed()或类似的东西。用户通过该界面输入电话号码的数字/字符。


显然是phone.Dial(number)


如果您正在写OO,那么您将从基本对象(不是数字)开始,该数字将进入电话,因此,通过phone.dial()这样,您还可以phone.answer()phone.disconnect()phone.powerOFF ,等。

另一种查看方式是电话拨打号码还是电话号码拨打电话?


我根本不会将电话号码作为一个类,因为它没有任何行为,它只是一个数据元素。


phone.dial()+1。

PhoneNumber的变体状态或行为是什么?唯一想到的就是"拨号规则"(如果在外面,请拨打国家代码,拨打" 9"以拨打外线,等等)。这种情况似乎很适合电话。

如果您的对象模型不需要方差-数字只是一个数字序列,那么"拨号"就是foreach(电话号码中的数字){press(digit);我和Rob Conery在一起:嗯。


我不确定这与电子表格难题有何关系。您希望将来使用电话拨打帐号吗?要在计算器上使用电话号码?您的"未来需求准备"示例不是很好。

另外,您使用动词" dial"。当然,我可以想象在电话上"拨号"一个帐号。 (但是,这是很大的努力。)但是,如果要在计算器上使用此电话号码,您会将此操作称为"拨号"吗?如果函数的名称根据传递的参数类型而变化,则说明存在设计错误。

在典型的OO设计中,对象获取的消息带有数据,而不是相反。


在这里不要成为消极的问题,但是这类问题非常学术性。这完全取决于应用程序。我可以想出以任何一种方式做的很好的理由,而且我已经看到太多的优秀程序员陷入了这种无聊的设计细节中。


推荐阅读