- 浏览: 78647 次
- 性别:
- 来自: 杭州
最近访客 更多访客>>
文章分类
最新评论
-
ieniac:
516456267 写道离线版非常好使!
查看当前本机浏览器FlashPlayer版本 -
516456267:
离线版非常好使!
查看当前本机浏览器FlashPlayer版本
Alec Sharp, in the recent book Smalltalk by Example [SHARP], points up a very valuable lesson in few words:
Procedural code gets information then makes decisions. Object-oriented code tells objects to do things.
— Alec Sharp
That is, you should endeavor to tell objects what you want them to do; do not ask them questions about their state, make a decision, and then tell them what to do.
The problem is that, as the caller, you should not be making decisions based on the state of the called object that result in you then changing the state of the object. The logic you are implementing is probably the called object’s responsibility, not yours. For you to make decisions outside the object violates its encapsulation.
Sure, you may say, that’s obvious. I’d never write code like that. Still, it’s very easy to get lulled into examining some referenced object and then calling different methods based on the results. But that may not be the best way to go about doing it. Tell the object what you want. Let it figure out how to do it. Think declaratively instead of procedurally!
It is easier to stay out of this trap if you start by designing classes based on their responsibilities, you can then progress naturally to specifying commands that the class may execute, as opposed to queries that inform you as to the state of the object.
Just the Data
The main purpose of this exercise is to ensure a correct division of responsibility that places the right functionality in the right class without causing excess coupling to other classes.
The biggest danger here is that by asking for data from an object, you are only getting data. You’re not getting an object—not in the large sense. Even if the thing you received from a query is an object structurally (e.g., a String
) it is no longer an object semantically. It no longer has any association with its owner object. Just because you got a string whose contents was “RED”, you can’t ask the string what that means. Is it the owners last name? The color of the car? The current condition of the tachometer? An object knows these things, data does not.
The fundamental principle of Object Oriented programming is the unification of methods and data. Splitting this up inappropriately gets you right back to procedural programming.
Invariants aren’t enough
Every class has invariants—things that must always be true. Some languages (such as Eiffel) provide direct support for specifying and checking invariants. Most languages do not, but that only means that the invariants are not stated—they still exist. For instance, an iterator has the invariant that (using Java as an example):
hasMoreElements() == true
// implies that:
nextElement()
// will return a value
In other words, if hasMoreElements()
is true, then attempting to get the next element must succeed, or something is seriously broken. If you are running multi-threaded code without the proper synchronization (locking), it may well be that the above invariant doesn’t hold: some other thread grabbed the last element before you did.
The invariant doesn’t hold; so something is wrong—you have a bug.
According to Design by Contract, as long as your methods (queries and commands ) can be freely intermixed, and there is no way to violate the class invariant by doing so, then you are ok. But while you are maintaining the class invariant, you may have also dramatically increased the coupling between the caller and the callee depending on how much state you have exposed.
For instance, suppose you have a container object C
. You could expose iterators for the held objects in this container, as many of the JDK core routines do, or you could provide a method that would run some function over all members in the collections for you. In Java you might declare this as something like:
public interface Applyable {
public void each(Object anObject);
}
...
public class SomeClass {
void apply(Applyable);
}
// Called as:
SomeClass foo;
...
foo.apply( new Applyable() {
public void each(Object anObject) {
// do what you want to anObject
}
});
(Forgive the neologic barbarism of “Apply-able”; we’ve found it handy to name interfaces as “-able”, but English isn’t as cooperative as one would like).
This is easier to code in languages with function pointers, and even easier in Perl or Smalltalk where such concepts are built in, but you should get the idea: “run this function over all contained items, I don’t care how.”
You can achieve the same results both ways, either via an apply
sort of method or via iterators. The choice really comes down to how much coupling you are willing to have: To minimize coupling, expose the minimum amount of state necessary. As shown here, apply
exposes less state than exposing an iterator does.
Law of Demeter
So we’ve decided to expose as little state as we need to in order to accomplish our goals. Great! Now within our class can we just starting sending commands and queries to any other object in the system will-nilly? Well, you could, but that would be a bad idea, according to the Law of Demeter. The Law of Demeter tries to restrict class interaction in order to minimize coupling among classes. (For a good discussion on this topic, see [APPLETON]).
What that means is that the more objects you talk to, the more you run the risk of getting broken when one of them changes. So not only do you want to say as little as possible, you don’t want to talk to more objects than you need to either. In fact, according to the Law of Demeter for Methods, any method of an object should only call methods belonging to:
- itself.
- any parameters that were passed in to the method.
- any objects it created.
- any composite objects.
Specifically missing from this list is methods belonging to objects that were returned from some other call. For example (we’ll use Java syntax here):
SortedList thingy = someObject.getEmployeeList();
thingy.addElementWithKey(foo.getKey(), foo);
This is what we are trying to prevent. (We also have an example of Asking instead of Telling in foo.getKey()
). Direct access of a child like this extends coupling from the caller farther than it needs to be. The caller is depending on these facts:
- someObject holds employees in a SortedList.
- SortedList’s add method is
addElementWithKey()
- foo’s method to query its key is
getKey()
Instead, this should be:
someObject.addToThingy(foo);
Now the caller is only depending on the fact that it can add a foo
to thingy
, which sounds high level enough to have been a responsibility, not too dependent on implementation.
The disadvantage, of course, is that you end up writing many small wrapper methods that do very little but delegate container traversal and such. The cost tradeoff is between that inefficiency and higher class coupling.
The higher the degree of coupling between classes, the higher the odds that any change you make will break something somewhere else. This tends to create fragile, brittle code.
Depending on your application, the development and maintenance costs of high class coupling may easily swamp run-time inefficiencies in most cases.
Command/Query Separation
Now back to to the ask vs. tell thing. To ask is a query, to tell is a command. I subscribe to the notion of maintaining these as separate methods. Why bother?
- It helps to maintain the “Tell, Don’t Ask” principle if you think in terms of commands that perform a very specific, well defined action.
- It helps you to think about class invariants if you class is primarily command based. (If you are just tossing data out, you probably aren’t thinking much in the way of invariants).
-
If you can assume that evaluation of a query is free of any side effects, then you can:
- use queries from within a debugger without affecting the process under test.
- create built-in, automatic regression tests.
- evaluate class invariants, pre- and post-conditions.
The last, of course, is why Eiffel requires only side-effect free methods to be called from within an Assertion. But even in C++ or Java, if you want to manually check the state of an object at some point in the code, you can do so with confidence if you know the queries you are calling will not cause anything else to change.
References
[SHARP] Sharp, A. “Smalltalk By Example” McGraw-Hill, 1997.
[APPLETON] Appleton, B. Introducing Demeter and Its Laws
发表评论
-
Sublime Text 2 Custom Layout
2012-10-15 15:21 7011. Edit Sublime Text 2\Data ... -
Linux Tips
2012-10-15 06:43 601-- How to config ip in Linux F ... -
Change FireBug DEFAULT font-family
2011-12-31 16:44 7681. Search *firebug*.xpi in Fire ... -
cygwin for e-texteditor
2011-08-17 11:03 667install cygwin for ete Se ... -
Config Remark
2011-06-19 15:06 8841. version control 2. out o ... -
IE associate Fix
2010-08-11 09:33 644注册动态链接库 for %%i in (c:\windows ... -
qq.foxmail.crack
2009-10-20 17:31 933javascript:var s=/mail.qq.com/; ... -
Editing/Authoring Actionscript3 Classes In IntelliJ IDEA
2009-08-25 17:54 2058I wanted to see whether it was ... -
Flex & RIA
2009-08-22 23:28 797Framework: a. Cairngorm [MVC] ... -
Never, never, never use String in Java (or at least less often :-)
2009-08-22 19:24 1029Never, never, never use (unwrap ... -
Extract flash from Excel or Word
2009-06-05 14:34 853打开含有Flash的EXCEL文档, 点击"视图& ... -
Google的九条创新原则
2009-06-04 11:57 737Innovation, not instant perfec ... -
Ten Quotients of Success
2009-05-29 10:50 1011成功是每一个人都梦想 ... -
How to Rate a Software Developer
2009-05-29 10:42 968How do you rate a software deve ...
相关推荐
2. Ask him 变为 "Don't ask him." 3. Please wait for her 变为 "Please don't wait for her." 4. Read the book carefully 变为 "Don't read the book carelessly." 5. Sit under the tree, please 变为 "Don't ...
在IT行业中,"Tell Don't Ask"原则是一个重要的设计哲学,尤其在面向对象编程中被广泛推崇。这个原则源于Smalltalk社区,由Martin Fowler在其著作《重构:改善既有代码的设计》中进行了阐述。"Tell Don't Ask"的核心...
祈使句通常用来表达命令、请求、劝告、警告或禁止等,其基本结构是动词原形加上其他成分,或者以"Please"或"Don't/Never"开头。 1. **祈使句的基本形式**: - 简单祈使句:如 "Go and wash your hands." 这种结构...
- Don't ask me to help you. You must rely on yourself. (不要让我帮你!你必须要靠自己。) 6. **Tell sb. to do/ tell sb. not to do**:这两个结构表示“告诉某人做/不要做某事”。 - Let me tell you how ...
- Don’t ask me to help you; you must rely on yourself. (不要让我帮你的忙!你必须要自己解决问题。) - My mother asks us not to eat anything before we go to bed. (妈妈让我们睡觉前不吃东西。) 6. tell ...
- 祈使句引导的宾语从句,肯定形式为"ask/tell… +sb. +to +v.",否定形式为"ask/tell/…+sb.+ not to + v.",如"Could you tell me how often you go to the library?" 或者 "Ask him not to forget the meeting....
- Don’t **ask me to help you**; you must **help yourself**. (不要让我帮你的忙!你必须要自己解决问题。) - My mother **asked us not to eat anything** before we go to bed. (妈妈让我们睡觉前不吃东西。)...
7. 建议或禁止类,直接引语中的"Don't"在间接引语中保持不变,即`said, "Don't smoke any more."` 8. 请求类,祈使句变为let sb. do结构,时间状语today不变,即`Let me go home earlier today.` 9. 请求类,ask ...
for sth.** - 请求或要求得到某物,如"Don’t always ask your parents for money."告诫不要总是向父母要钱。 5. **ask/tell sb. how to do sth.** - 教导别人如何做某事,如"I asked /told the man how to get to...
例如,第1题"“Don't play games in the classroom, ” the monitor said to us." 转换成 "The monitor told us not to play games in the classroom." 这里时态由一般现在时变为一般过去时,祈使句"Don't" 变成了 ...
- 鼓励多提问:"Don't be afraid to ask a lot of questions." - 对他人的到来表示欢迎:"I hope you'll be happy working here." 6. **词汇词性与搭配**: - "Isn't it impolite to call people by their first...
4. "I don’t know how told you the news." 应改为"I don’t know how to tell you the news." know后面跟不定式表示知道如何做某事。 5. "Would you like us seeing Beijing Opera?" 应改为"Would you like us to...
在英语句子中,主句通常会包含一个谓语动词,如"tell"、"ask"、"know"等,此时如果动词后面跟的句子是作为这个动词的宾语,并且这个句子是一个完整的陈述句,那么这个句子就被称为宾语从句。 1. **语序**:在宾语...
- Sorry, I don’t know. I’m new here, too. (对不起,我不知道。我也是刚到这儿的。) - I'm sorry, I'm not sure. You'd better ask the policeman over there. (对不起,我不能确定,你最好问那边的警察。...
当动词如allow, ask, tell等后面跟不定式作宾语补足语时,"to"也可以省略。例如: - "Don't touch the light unless your mother allows you to" 可以简化为 "Don't touch the light unless your mother allows ...
因此,我们决定采用一种新的编程范式并探索 tell don't ask 的局限性。告诉不要问 - 一个相当极端的定义如果没有信息从被调用者返回给调用者,则给定的函数调用是 TDA。 这大致意味着: 没有返回类型不传递具有任何...