转贴一段Robert C. Martin的帖子


最近开始认真看新闻组comp.object,发现Robert C. Martin 这位大师经常在那里出没,这是他老人家近期的一个回帖,觉得他老人家在强调不要教条式的学习pattern,而是要用refactoring,用unit-test来实践中学习design patterns.


Having read through the first half of your post it seems that you are
trying to couple MVC, Observer, Decorator, and Chain-of-Command
together in a single collaboration. I think you need to find a much
simpler solution.

As for your boss not liking a solution because it's not the "proper
form" of a pattern, he needs to get over it. Design patterns often
have to be massaged to fit in a particular situation. While there is
certainly some value in using canonical forms, it is often
impractical. That's why there are so many variations in the GOF book.
No one should think that those variations are all there are.

As for finding a simpler solution, I strongly recommend that you get
something very simple working, and then slowly add one feature at a
time. Very often you gain better insight into the design of a system
by implementing it in tiny steps, than you do by trying to solve the
patterns jigsaw puzzle. You'll still use patterns, but they'll be
supported by the real data of implementation instead of conjecture and
dogma.

And write your unit tests. Surround your effort with lots and lots of
unit tests. Use them to allow you to make changes to your code so
that you can explore the different pattern structures without fear of
breaking things that already work.


bdinmstig@hotmail.com (bdinmstig) might (or might not) have written
this on (or about) 3 May 2003 20:28:55 -0700, :

>I am relatively new to design patterns and have just joined a project
>team that uses them extensively. I have been debating with my new boss
>(who is also the technical architect and well versed in design
>patterns) all week over the following problem - I need the community's
>thoughts, please:
>
>I have thoroughly read and think I understand both the Decorator
>(Wrapper) and Observer patterns. My problem arose when I tried to
>Decorate a class that is the Subject in an Observer pattern (ie. it
>registers and informs listeners of events).
>
>I am designing a Model (as in M-V-C) to contain data that will be
>presented on a grid-like table component in the View. Let's call it
>TableModel. I wrote the class to hold the data, and register Views
>that want to know if the data is updated. Fine.
>
>I decided to use the Decorator pattern to provide filtering and
>sorting capabilities as needed. It seemed simple enough - the
>TableModelDecorator classes translate grid coordinates from View to
>TableModel whenever the View requests the data at row x, column y. The
>ConcreteTableModel doesn't know it's being Decorated, and the View
>doesn't know it's accessing a TableModelDecorator. Plus, multiple
>filters and column sorts can be added as needed at runtime by
>attaching more TableModelDecorator instances. Cool. So the class
>relationships look like this:
>
> TableModel
> __________|__________
> | |
> ConcreteTableModel TableModelDecorator
> __________|__________
> | |
> TableFilterDecorator TableSortDecorator
>
>The problem is now how do data update events propagate from the
>TableModel to the View?
>
>After a whole week of long debates and sleepless nights, I have
>identified FOUR possibilities. The first two are:
>
>1. The View is registered for events directly with the
>ConcreteTableModel instance. (This seems to be how most examples using
>GUI components work.)
>
>2. The View is registered for events with the outermost
>TableModelDecorator, which fires events when a data-modifying method
>is called on its TableModel interface.
>
>At first, I assumed option #1 was "how it's done" since all the
>examples of Decorator I have seen are GUI-centric and over-simplified.
>But since my TableFilterDecorator and TableSortDecorator objects would
>perform horribly without maintaining an internal mapping of View rows
>to Model rows, they both need to know when the underlying data has
>changed so they can update their maps. Another problem is that the
>"source" of the event is a different TableModel instance than the one
>the View knows about.
>
>My boss suggested I try option #2, which would allow each decorator to
>update its internal map. However the problem with this option is that
>it forces concrete implementations of TableModel to be "dumb" Models
>whose data only changes in response to external method calls. It would
>be impossible to create a model that can change its own data by other
>means. (For example, my ConcreteTableModel reacts to messages
>broadcast over a network and updates its data to reflect the content
>of these messages.)
>
>I found an example of my situation (for sorting, anyway) in the Java
>SDK examples. Sun's Philip Milne gave me option #3:
>
>3. TableModelDecorator implements the TableModelListener interface
>(the interface for objects that register with TableModel objects for
>updates). The View is registered with the outermost
>TableModelDecorator, which is in turn registered with the object it
>decorates. So if I have a ConcreteTableModel instance tm, Decorated by
>a TableFilterDecorator instance fd, Decorated by a TableSortDecorator
>instance sd - when tm's data changes it notifies its listener fd,
>which notifies its listener sd, which notifies its listener the View.
>
>It was option #3 that started all the debates. I like it because it
>combines the advantages of options #1 and #2 - the ConcreteTableModel
>can change by itself, and all TableModelDecorator instances are
>notified in order of their proximity to the ConcreteTableModel. (The
>order is important because each TableModelDecorator must update its
>map relative to the object it Decorates, which can be another
>Decorator with its own map. On further examination, I found this
>option has other benefits:
>
> a) Each decorator can construct its own event with itself as the
>"source" - the same object instance the View knows about.
>
> b) Adding/removing Decorators at runtime is easy - the setModel
>method of TableModelDecorator simply registers itself as a listener to
>the specified TableModel, and keeps a reference to that TableModel so
>it can Decorate it as normal. If it was previously attached to a
>different TableModel, it de-registers itself so it only gets events
>from the new TableModel.
>
> c) By declaring standard grid coordinate translation methods in
>TableModelDecorator (eg. getModelRow and getModelColumn), all
>TableModel methods can be implemented in TableModelDecorator, so the
>concrete Decorators need only override the translation methods and
>TableModelListener methods. Internal map updates are done in the
>TableModelListener methods in reaction to events from the Model. In
>other words, the Decorators can concentrate on Decorating.
>
> d) Unlike option #2, it is possible for one Model to be shared among
>multiple views, and each view can access the Model through a different
>combination of Decorators. For instance, in the example above, fd can
>be simultaneously Decorated by a second TableFilterDecorator fd2,
>which can be Decorated by a second TableSortDecorator sd2, which can
>register a second View for updates. The result is a hierarchy of
>Decorators and Views rooted in a single Model instance. Of course, it
>is possible to achieve the same result with the standard Decorator
>pattern, however unlike the first two, option #3 works with events
>propagating from the Model as well. In the example, tm notifies its
>listener fd, which notifies both its listeners sd and fd2, which
>notify their listeners the first View and sd2, respectively. Finally,
>sd2 notifies its listener the second View. Meanwhile, all the
>Decorators' internal maps are updated in the correct order thanks to
>the "chained" propagation of events.
>
>So what's wrong with this? (Actually that's the question I need an
>answer to!) My boss didn't like option #3 because it's not the
>standard Decorator pattern. Actually, the Decorator pattern (as
>presented in the GoF book anyway) doesn't provide any guidance for
>Decoratees that are also the Subject in an Observer pattern. He says
>it would be confusing to people who are accustomed to the Decorator
>pattern. (Is this true? Anyone out there who is familiar with
>Decorator find this confusing?)
>
>He also says it is the TableModelListener that is being decorated,
>rather than the TableModel. I thought about this one, and realised
>that in a way he is correct on this point because if we add the View
>into the picture it acts as a concrete implementor of the
>TableModelInterface, which is also implemented by a Decorator class:
>
> TableModel TableModelListener
> __________|__________ __________|__________
> | | |
> ConcreteTableModel TableModelDecorator (the View)
> __________|__________
> | |
> TableFilterDecorator TableSortDecorator
>
>So, I thought, what we really have is TWO Decorator patterns joined at
>the hip - maybe we can call this the "Siamese Twin" Decorator pattern.
>;)
>
>But as I write this, it has just occurred to me that the Decorator
>pattern doesn't allow a Decorator to Decorate multiple Decoratees -
>but a TableModel registers 0..n listeners!
>
>The event propagation model could also be described as a variation of
>Chain of Command - the variation being that the chain splits into
>branches as described above, so successive Handlers are added &
>removed rather than being simply set. As in Chain of Command, it is
>conceivable that a TableModelDecorator "Handles" an event and stops it
>without passing it on to its "successors" (registered listeners) - for
>example, TableFilterDecorator needn't notify its listeners if the
>change to the underlying data doesn't affect the post-filter view of
>the data.
>
>My boss is not the type to criticise a design without offering an
>alternative solution - his was option #4:
>
>4. The class relationships are similar to that of option #3, but
>events propagate in the opposite direction. The outermost Decorator is
>registered with the Model for events, and its Decoratee is registered
>with it so that the innermost Decorator is the last to be notified.
>
>He says this makes for a more standard Decorator pattern because the
>listener methods become part of the interface for Decorators. But as
>far as I can tell, moving to this option gives up a lot of flexibility
>and provides none in return. For example, b) and c) above no longer
>work as smoothly and some code must be repeated for every Decorator.
>
>I have tried to modify my code to conform to his suggestion, but have
>had a lot of trouble especially with respect to the dynamic
>registering/deregistering of listeners when a Decorator is
>added/removed and the order with which TableModelDecorators get a
>chance to update their internal maps. Basically, I find option #4 much
>MORE confusing than option #3! I would like to know if this is all
>worth it - or if there is another option we haven't considered that is
>as elegant as option #3 but does not depart too much from the standard
>patterns.
>
>If you are reading this sentence, I am extremely grateful to you for
>taking the time to get through all my poorly written guff. I eagerly
>await the community's opinion!
>
>Thanks in advance.

Robert C. Martin | "Uncle Bob"
Object Mentor Inc.| unclebob @ objectmentor . com
PO Box 5757 | Tel: (800) 338-6716
565 Lakeview Pkwy | Fax: (847) 573-1658 | www.objectmentor.com
Suite 135 | | www.XProgramming.com
Vernon Hills, IL, | Training and Mentoring | www.junit.org
60061 | OO, XP, Java, C++, Python |

对,这也是我的实践经验(不是附庸风雅),所以我才写了一篇refactor到设计模式的文章,以前我是说“用设计模式去refactor”,有朋友指出我的说法不对,应该是refactor to pattern。

我还是坚持我的观点,因为:
大部分要refactoring都是因为我发现了这里面隐含了设计模式,所以,有了思路后,我开始去refactor,所以叫用设计模式去refactor。

我的文章见:
http://www.jdon.com/refactoring/register.htm