【问题标题】:Design question: does the Phone dial the PhoneNumber, or does the PhoneNumber dial itself on the Phone?设计问题:电话是拨打电话号码,还是电话号码在电话上自行拨打?
【发布时间】:2008-09-16 01:41:13
【问题描述】:

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

一切都一样,你写phone.dial(phoneNumber)还是phoneNumber.dialOn(phone)?请记住未来可能的要求(除了电话号码之外的帐号、除电话之外的计算器)。

这个选择倾向于说明信息专家、单一职责原则和告诉不要问的习语是如何相互矛盾的。

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

如果您熟悉 Ken Pugh 在 Prefactoring 方面的工作,这是Spreadsheet Conundrum;你是添加行还是列?

【问题讨论】:

  • 关于新要求——假设您现在拥有寄存器和计算器。现在你需要 register.dial(phoneNumber) 和 calc.dial(phoneNumber)。使用 phoneNumber.dialOn(DialableDevice),没有新方法,只有两个新类(如果您有 Phone、Register 和 Calculator 实现 DialableDevice)。
  • 如果您认为如果新要求是关于拨打新类型的号码(例如帐号和 SSN),那么您是正确的,那么我刚才给出的论点就被颠倒了。这两种情况是“正交的”,因此电子表格难题参考。
  • 未来可能的需求?雅格尼!
  • 我仍然认为你需要一个 CallController 来连接设备 ;-)
  • 致所有回答的人,感谢您的意见。这是一个没有正确答案的问题(不是全部),对我来说,吸收每个人的答案背后的推理是很有教育意义的。

标签: oop


【解决方案1】:

phone.dial(),因为拨打的是电话。

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

【讨论】:

    【解决方案2】:

    Meh - User.Dial(号码)。电话在给定的上下文中毫无意义。 SOL(大声说出来)是一种很好的思考方式(除了习语和原则):

    电话有拨号盘。他们不能自己拨号。 电话号码是数字。 用户通过电话拨号拨打电话号码。

    【讨论】:

    • 我不认为我们在谈论旋转电话。任何带有内置电话簿的手机都可以自行拨号。 User.Call(contact) -> Phone.Dial(number)
    【解决方案3】:

    问题假设了答案的上下文,因此造成了错误的困境

    在此示例中,“电子表格难题”是一种错误的二分法:行和列是表示层,不一定是数据层。下面的 cmets 告诉我我误解了这个类比,但我不这么认为——说“应该是行还是列,哪一个更有可能改变”是在问题空间上强制进行不必要的选择——它们都是同样可能改变。在这个具体的例子中,这会导致为解决方案选择错误的 [yes wrong] 范式。拨打电话是旧机械设备如何启动与另一个旧机械设备的连接;对于现代电话来说,这几乎不是一个恰当的类比。并且假设有一个“用户”来发起呼叫只是移动了问题 - 尽管它朝着正确的方向移动它,即远离旋转电话模型;-)

    如果您查看 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 协议以获取更多示例

    【讨论】:

    • 你误解了这个类比。它不是一个字面的电子表格,而是您在行和列上排列类,并查看未来哪一个更有可能发生变化:行(电话、寄存器、计算器)或列(电话号码、帐号、SSN )。
    • 问题假设了答案的上下文,因此造成了错误的困境;见上面的编辑
    • 我同意这似乎非常适合服务或控制器。通常,当我在两个设计选择之间陷入困境并且没有一个比另一个更适合时 - 我倾向于认为我还没有看到“正确”的选择。
    【解决方案4】:

    选择是给列对象还是行对象 dial 方法不会改变程序的缩放方式。

    dial 方法本身就是一系列行和列方法。你必须问这些方法依赖于什么。

    如果行方法的序列不依赖于确切地知道涉及哪个列对象(但确实取决于涉及哪个特定的行对象)并且对于列方法的序列反之亦然,那么问题的规模为 m + n(m = num.rows,n = num.cols)。当您创建一个新行时,如果为 column 方法分配了“dial”方法,它实际上并没有为您节省任何工作。您仍然必须指定唯一的行方法序列以在某处的“拨号”中使用!

    但是,如果说 'dial' 中的列方法序列甚至不依赖于涉及哪个 column 对象(它们使用一个“通用”列方法序列),那么问题只是规模为 m。是否将“dial”方法分配给列对象实际上并不重要,程序仍按 m 缩放;在添加 1 个列对象时,基本上不需要创建新的 dial 方法,并且您显然可以选择将所有这些 dial 方法本身抽象为一个通用的 dial 方法。

    【讨论】:

    • 我同意,这取决于,它肯定有助于抽象行和列的类型以帮助可扩展性。
    【解决方案5】:

    如果您编写 OO,那么您从基本对象开始,它不是号码,号码将进入电话,所以 phone.dial() 这样您也可以 phone.answer() phone.disconnect() phone.powerOFF 等

    另一种看待它的方式是电话拨打号码还是号码拨打电话?

    【讨论】:

      【解决方案6】:

      很明显,phone.Dial(number)

      【讨论】:

        【解决方案7】:

        两者都没有。用户在电话上拨打电话号码。

        【讨论】:

        • 除了闻到程序的味道,在那个方法里面,问题仍然存在:你叫哪个:phone.dial(phoneNumber) 还是 phoneNumber.dialOn(phone)?
        • 更像是:“for each(phoneNumber 中的号码) phone.dialNumber(number)”。电话没有理由了解电话号码是什么(除了面向对象之外,请考虑诸如必需与非必需区号、国际电话等之类的事情)。删减格式。
        【解决方案8】:

        很明显,您可以从 PhoneUserFactory.CreatePhoneUser() 方法中获得实现的 PhoneUserInterface 接口有一个方法 dial(Phone, Number) 可以用来拨打电话。

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

        【讨论】:

        • 对,那代码调用哪一个:phone.dial(phoneNumber) 或 phoneNumber.dialOn(phone)?
        【解决方案9】:

        A:phone.dial(phone_number)

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

        • 电话是否已经在通话? (如果是/否,该怎么办?)
        • 如果拨号方式发生变化会怎样? (全球漫游、不同运营商等)
        • 另外,范围呢?拨打电话时,需要将电话号码添加到最近拨出电话列表中。

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

        我会说 Steven A. Lowe 已经做到了。这应该由 Controller 类型的对象来完成,以处理不同的状态等。让您的 PhoneNumber 对象保持沉默,并将智能交给需要担心保持电话嗡嗡声的中间人。

        【讨论】:

          【解决方案10】:

          这里不是否定的,但这类问题非常学术。这完全取决于应用程序。无论哪种方式,我都能想到很好的理由,而且我见过太多优秀的程序员陷入这种没有意义的设计细节中。

          【讨论】:

            【解决方案11】:

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

            另外,您使用动词“拨号”。当然,我可以想象在电话上“拨打”一个帐号。 (不过,这是一个很大的延伸。)但是如果这个电话号码要在计算器上使用,你会称这个动作为“拨号”吗?如果函数的名称根据传递的参数类型而改变,则说明设计错误。

            在典型的 OO 设计中,对象会收到携带数据的消息,而不是相反。

            【讨论】:

            • 我没有意识到个人答案有 cmets;请查看我在原始问题上留下的第一条评论。谢谢。
            【解决方案12】:

            phone.dial() +1。

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

            如果您的对象模型不需要变化——数字只是一个数字序列,“拨号”只是 foreach(digit in phonenumber) { press(digit); } 我和 Rob Conery 在一起:嗯。

            【讨论】:

              【解决方案13】:

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

              【讨论】:

              • 109578390258792836 呢?这是电话号码吗?国家代码是多少?如果是美国,区号是多少?是否需要先拨 9 才能接通外线?
              猜你喜欢
              • 2018-03-09
              • 1970-01-01
              • 1970-01-01
              • 2016-08-11
              • 2015-01-31
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-03-26
              相关资源
              最近更新 更多