设计问题:电话是拨打电话号码,还是电话号码在电话上自行拨号?
这是我在DDD Yahoo!上发布的内容重新发布的。团体。
在所有条件都相等的情况下,我们编写的是phone.dial(phoneNumber)还是phoneNumber.dialOn(phone)?请记住将来可能的要求(除了电话号码之外的帐号,除了电话之外的计算器)。
这种选择倾向于说明信息专家,单一责任原则和告诉不要问的成语如何相互矛盾。
phoneNumber.dialOn(phone)支持信息专家和告诉不要问,而phone.dial(phoneNumber)支持单一责任原则。
如果我们熟悉Ken Pugh在Prefactoring中的工作,这就是Spreadsheet难题;我们添加行还是列?
解决方案
回答
phone.dial(),因为是进行拨号的电话。
Actor.Verb(输入)->输出。
回答
如果我们正在写OO,那么我们将从基本对象(不是数字)开始,该数字将进入电话,因此,通过phone.dial()这样,我们还可以phone.answer()phone.disconnect()phone.powerOFF ,等等。
另一种查看方式是电话拨打电话号码还是电话号码拨打电话?
回答
显然是phone.Dial(number)
回答
Meh User.Dial(number)。在给定的上下文中,电话毫无意义。 SOL(大声说)是一种很好的方式来思考这一问题(不包括成语和原则):
电话有拨号盘。他们无法拨号。
电话号码是数字。
用户在电话拨号盘上拨打电话号码。
回答
在这里不要成为消极的问题,但是这类问题是非常学术性的。它完全取决于应用程序。我可以想出以任何一种方式做的很好的理由,而且我已经看到太多的优秀程序员陷入了这种无聊的设计细节中。
回答
我不确定这与电子表格难题有何关系。我们希望将来使用电话拨打帐号吗?要在计算器上使用电话号码?"未来需求准备"示例不是很好。
另外,我们使用动词" dial"。当然,我可以想象在电话上"拨号"一个帐号。 (但是,这是很大的努力。)但是,如果要在计算器上使用此电话号码,我们会将此操作称为"拨号"吗?如果函数名称根据传递的参数类型而变化,则说明存在设计错误。
在典型的OO设计中,对象获取的消息带有数据,而不是相反。
回答
phone.dial()+1.
PhoneNumber的变体状态或者行为是什么?唯一想到的就是"拨号规则"(如果在外面,请拨打国家代码,拨打" 9"以拨打外线等)。这种情况似乎很适合电话。
如果对象模型不需要方差-一个数字只是一个数字序列,那么"拨号"就是foreach(电话号码中的数字){press(digit);我和Rob Conery在一起:嗯。
回答
显然,可以从PhoneUserFactory.CreatePhoneUser()方法获得实现的PhoneUserInterface接口具有可用于拨打电话的方法dial(Phone,Number)。
编辑:回答评论。两者都不。手机应该有一个buttonPressed()或者类似的东西。用户通过该界面输入电话号码的数字/字符。
回答
两者都不。用户在电话上拨打电话号码。
回答
该问题假定答案的上下文,从而造成错误的困境
在此示例中,"电子表格难题"是错误的二分法:行和列是表示层,不一定是数据层。下面的评论告诉我,我误解了这个类比,但我不认为说"这应该是一行还是一列,更可能会改变这一行",迫使他们在问题空间上进行了不必要的选择,它们都同样可能改变。在此特定示例中,这导致为解决方案选择错误的(是错误的)范例。拨打电话是旧的机械设备如何启动与另一个旧的机械设备的连接。这几乎不是现代电话的恰当比喻。并假设有一个"用户"发起呼叫,尽管问题沿正确的方向移动,也就是简单地解决了问题,即远离旋转电话型号;-)
如果我们看一下TAPI的工作原理(很抱歉输入错误,它是TAPI而不是ATAPI!),那么在某种意义上,我假设有一个相当于"用户"的呼叫控制器,它可以管理设备之间的连接。一个设备不呼叫另一设备,呼叫控制器连接设备。因此,以下示例在本质上仍然是正确的。使用CallController对象而不是通用Connection可能更正确,但是类比应该足够清晰。
在此示例中,电话是地址也称为"电话号码"的设备。 "拨号"操作员在两个设备之间建立连接。所以答案是:
Phone p1 = new Phone(phoneNumber1); Phone p2 = new Phone(phoneNumber2); Connection conn = new Connection(p1,p2); conn.Open(); //...talk conn.Close();
通过重载Connection以包括设备或者其他连接的列表(例如,
Connection confCall = new Connection(p1,p2,p3,p4,p5,p6); confCall.Open(); Connection joinCall = new Connection(confCall,p7,p8,conn); joinCall.Open();
查看TAPI协议以获取更多示例
回答
我根本不会将电话号码作为一个类,因为它没有任何行为,它只是一个数据元素。
回答
答:phone.dial(phone_number)
PhoneNumber是愚蠢的,只是一个数据集。当发生"拨号"时,PhoneNumber对象应该知道如何拨号吗?有许多州需要跟踪,例如:
- 电话已经在通话吗? (如果是/否,该怎么办?)
- 如果更改拨号方式会怎样? (全球漫游,其他运营商等)
- 还有,范围呢?拨打电话时,需要将电话号码添加到最近的拨出电话列表中。
如果PhoneNumber对象需要了解所有这些信息,则它不是DRY,并且代码会更少
携带方便,更容易折断。
我想说的是史蒂文·A·洛(Steven A.Lowe)。这应该由Controller类型的对象来处理,以处理不同的状态,等等。让PhoneNumber对象保持愚蠢状态,并向需要担心保持电话嗡嗡声的中间人提供技巧。
回答
选择是给列对象还是行对象提供表盘方法,都不会改变程序的缩放比例。
Dial方法本身将只是行和列方法的序列。我们必须问这些方法依赖什么。
如果行方法的顺序不取决于确切地知道涉及哪个列对象(而是取决于所涉及的特定行对象),反之亦然,那么列方法的问题将按m + n(m =数行,n =列数)。创建新行时,如果为列方法分配了"拨号"方法,那么它实际上不会为我们节省任何工作。我们仍然必须指定行方法的唯一序列,以便在某处的"拨号"中使用!
但是,如果说"拨号"中的列方法序列甚至不取决于所涉及的列对象(它们使用一个"通用"列方法序列),那么问题只会扩展为m。我们是否为列对象分配了"拨号"方法实际上并不重要,程序仍会缩放为m;当再添加1个列对象时,基本上不需要做任何工作来制作新的拨号方法,并且我们显然可以选择将所有这些拨号方法本身抽象为一个通用的拨号方法。