Design question: does the Phone dial the PhoneNumb

2019-02-09 16:19发布

This is re-posted from something I posted on the DDD Yahoo! group.

All things being equal, do you write phone.dial(phoneNumber) or phoneNumber.dialOn(phone)? Keep in mind possible future requirements (account numbers in addition to phone numbers, calculators in addition to phones).

The choice tends to illustrate how the idioms of Information Expert, Single Responsibility Principle, and Tell Don't Ask are at odds with each other.

phoneNumber.dialOn(phone) favors Information Expert and Tell Don't Ask, while phone.dial(phoneNumber) favors Single Responsibility Principle.

If you are familiar with Ken Pugh's work in Prefactoring, this is the Spreadsheet Conundrum; do you add rows or columns?

标签: oop
13条回答
叼着烟拽天下
2楼-- · 2019-02-09 16:39

Meh - User.Dial(number). The phone is meaningless in the given context. SOL (speak out loud) is a nice way to think this through (idioms and principles aside):

Phones have a dial. They can't dial themselves. Phone numbers are digits. Users dial PhoneNumbers on a Phone Dial.

查看更多
\"骚年 ilove
3楼-- · 2019-02-09 16:39

Not to be the negative one here, but these kinds of questions are very academic. It completely depends on the application. I can think of very good reasons for doing it either way, and I've seen too many good programmers get bogged down in this kind of moot design details.

查看更多
你好瞎i
4楼-- · 2019-02-09 16:40

Choosing whether to give the column objects or the row objects the dial method doesn't change how the program will scale.

The dial method is just going to be itself a sequence of row and column methods. You have to ask what those methods depend on.

If the sequence of row methods doesn't depend on knowing exactly which column object is involved (but does depend on which particular row object is involved) and vice versa for the sequence of column methods, then the problem scales as m + n (m = num. rows, n = num. cols). When you create a new row it doesn't actually save you any work had the column method been assigned the 'dial' method. You still have to specify a unique sequence of row methods for use in 'dial' somewhere!

If, however, say the sequence of column methods inside 'dial' doesn't even depend on which column object is involved (they use one 'generic' sequence of column methods), then the problem just scales as m. It doesn't actually matter if you've assigned the 'dial' method to the column objects, the program still scales as m; essentially no work is required to make a new dial method when adding 1 more column object and you clearly have the option of abstracting all those dial methods themselves into one generic dial method.

查看更多
手持菜刀,她持情操
5楼-- · 2019-02-09 16:44

the question assumes the context of the answer, and thus creates a false dilemma

the 'spreadsheet conundrum' is a false dichotomy in this example: rows and columns are the presentation layer, not necessarily the data layer. The comments below tell me i misunderstood the analogy, but i don't think so - saying 'should this be a row or a column, which one is more likely to change' is forcing an unnecessary choice on the problem space - they are both equally likely to change. And in this specific example, this leads to choosing the wrong [yes wrong] paradigm for the solution. Dialing a phone is how old mechanical devices initiated a connection to another old mechanical device; this is hardly an apt analogy for modern telephony. And assuming that there is a 'user' to initiate the call simply moves the problem - although it moves it in the correct direction, i.e. away from the rotary-phone model ;-)

If you look at how the TAPI [sorry about the typo earlier, it's TAPI not ATAPI!] protocol works, there is a call controller - equivalent to the 'user' i suppose in some sense - that manages the connections between devices. One device does not call another, the call controller connects devices. So the example below is still essentially correct. It might be more correct to use a CallController object instead of a generic Connection, but the analogy should be clear enough as is.

In this example, a phone is a device with an address aka a 'phone number'. The 'dial' operator establishes a connection between the two devices. So the answer is:

Phone p1 = new Phone(phoneNumber1);
Phone p2 = new Phone(phoneNumber2);
Connection conn = new Connection(p1,p2);
conn.Open();
//...talk
conn.Close();

this will support multi-party calls as well, by overloading Connection to include a list of devices or other connections, e.g.

Connection confCall = new Connection(p1,p2,p3,p4,p5,p6);
confCall.Open();

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

look at the TAPI protocol for more examples

查看更多
太酷不给撩
6楼-- · 2019-02-09 16:45

phone.dial(), because it's the phone that does the dialing.

Actor.Verb( inputs ) -> outputs.

查看更多
Viruses.
7楼-- · 2019-02-09 16:50

phone.dial() +1.

What is the variant state or behavior of a PhoneNumber? The only thing that comes to mind are "dialing rules" (dial country code if outside, dial "9" to get outside line, etc.). That context seems well suited to the Phone.

If your object model doesn't require variance -- a number is just a sequence of digits, "dial" is just foreach(digit in phonenumber) { press(digit); } I'm with Rob Conery: meh.

查看更多
登录 后发表回答