COMP 204 - Patterns
Index
Factory Method
The factory method promotes loosely coupled code by replace instantiation requests for a specific object implementation with requests for a type of object adhering an interface. In common OOP languages this translates to replacing explicit new operations with requests for a more general type fulfilling the same interface requirements. The advantage is that different implementations of the specified interface may be substituted in without altering the code using the object; only the factory needs to be changed.
Proxy
It is unlikely that code will be simplified by adding a Proxy object. It is likely to take more code to keep track of a "promise" to create the object and then create it when it is actually needed. However, instantiating an object only when it is absolutely necessary can improve performance.
Strategy
If there is an explosion of strategy objects this simply means that they are lots of ways (code interchangeable) of performing a given algorithmic task. It might make sense to have the strategy objects managed by a factory. This would allow the decision of what type of strategy to use to be centralized.
I would have the Context passed to the Strategy conform to an interface that has all required data. This way, to perform the call at all would require that they Context provide the necessary data.
Decorator
It is no longer a decorator in the strict sense of the pattern. This is because a decorator is supposed to be a transparent wrapper around an object which augments its function, but does not alter they way it is interfaced. It is important that a decorator conform to the interface of the object it decorates because it is supposed to pretend to be it. In fact, the biggest advantage for decorators is that they can be nested adding a large number of possible configurations without explicitly coding all of the feature combinations. If they did not all use and conform to the same interface, then the stacking feature would not work.
Adapter
This type of adapter might be useful for a number of reasons. It could be used as a hook or logger to simply watch what calls are made to the object. It might also be used to slightly alter the behavior of the object. For example, we might wrap an object that behaves badly when given null pointers with a wrapper that simply checks for the null pointer case and handles it more gracefully. It is likely that the created object would indeed be considered a Proxy.
Bridge
A Bridge is useful when an interchangeable object is complicated and expected to be sub-classed itself. In the case of a Strategy, it is expected to stand alone and operate on a piece of data (the Context). Bridges are likely to be more persistent with multiple instances. Bridges are likely to be sub-classed and have complex stateful behavior by themselves.
Facade
The subsystem need not be complex. The objective of facade is simply to limit the interface to one or more objects. In the simplest case it is sufficient to be taking a complex object and restricting the set of operations permitted. For example, one might do this with a doubly linked list in order to make a FIFO. The original list might perform iteration operations and allow insertion and removal from both ends. The restricted interface could limit the interface to insertion at one end and removal from the other.
One might use a facade to prevent less skill programmers working on another part of a project from tampering with a complicated module. This could be offensive to people, albeit, reasonable.
Composite
The composite eliminates the need to check which interfaces children objects support and pick the correct one. All objects support the same interfaces, regardless of their specific type.
While this is a case where it is harder to sell the composite pattern, it would greatly depend on their function. If it is a GUI object, such as a window full of buttons, where we only have one window and lots of buttons, then the composite pattern still makes sense. Especially if under code reuse a new configuration might appear in the future with more nesting. However, there are many other cases where it simply might be, for example, one collection of some type of objects. In this case the composite pattern would be of little use.
Iterator
I would use an iterator. Specifically, something like a DFS iterator, that returns one loan at a time by traversing the composite tree in a DFS. This separates the traversal code and the code making decisions about the loan amounts.
Template Method
The same functionality could be done using composition in one of two was. The first is to make the abstract base classes functionality available by putting it in a concrete class and composing that in other objects. This seems a little counter-intuitive and awkward. It would also mean that all the implementing object would not adhere to the same interface necessarily. Another way would be to put the implemented methods in a class and compose that in the global object. In practice this might be done with (C++ style) templates as an efficient way to abstracting the functionality. This method breaks some run-time flexibility.
Abstract Factory
In general this would mean that a new factory method would have to be introduce.
A better solution might be to have the MakeRoom factory method take a (Command) object as a parameter which encodes the requested qualities for the returned object. This always a single factory method to create lots of different room types.
Builder
A given sub-class my not have any sub-class specific thing to do when an action is requested. In this case the sub-class will not have to be changed. Sub-classes only have code for methods where they have to handle specific cases, otherwise the general functionality of the parent object will be sufficient.
Singleton
The Singleton pattern might also be used with Builders. This is because Builders, like Abstract Factories, are used to generate other types of objects. Singleton might also be useful with Facade, often a sub-system is only instantiated once. Flyweight could be used with Singleton, because we wish to keep the number of actual instantiated objects small (1) and just keep references to the objects. If a proxy is used with something remote (like a database) it might make sense for it to be a Singleton. Mediator could might be a Singleton in for the same reasons as Facade. Lastly, Strategies often don't keep internal state but rather just act on some external objects. This makes them good candidate objects for the Singleton pattern.
Mediator
Since a task of a Mediator is to connect a bunch of sub-systems, it can become spaghetti code. A good way to alleviate this is to break off facades for related sub-systems. This can minimize the number of interfaces a Mediator needs to deal with.
Observer
I think it could makes sense for the View to talk to the Model. If the model conforms to well defined interface and there are many Views which primarily just display the data in the Model, than it might be silly for all of that data communication to go through the controller. I would have the Views hold direct references to the Models.
This type of system is highly event driven. It is useful to have an event logger to track down bugs in this type of system.
It is not clear. If there are queues in the system than delays may be unpredictable. In practice this is often handled with "soft failures" --- objects are careful before performing actions and if a requested action is invalid, then it is simply discarded. This type of behavior may or may not be acceptable for a given set of requirements.
Chain of Responsibility
A Chain of Responsibility simply goes down the list (recursively) looking for the first object to handle a task. Decorators, in general, will all perform an action, rather than just one. The Chain of Responsibility is not really a linked list because: it is often recursive and the objects are not just data but rather objects that perform a function on some parameter.
Seeing how patterns are programmed can help understand some of their applications and how they are related. However, in general a more broad understanding of the patterns is really ideal.
Memento
If a Memento object needs to be examined for it to be properly restored. For example, if it contains information about where it belongs, then this would break the encapsulation. In the ideal design the memento object i never to be open, just captured and restored.
Command
For the Command objects to have access to all of the application's data, the Execute method could take an instance of the application as a parameter. Another way to handle this would be to supply that instance when the Command object is created. In either case the reference to the application would have to be propagated up through the Menu and MenuItem objects.
Prototype
This pattern should be used when the creation of an object from scratch is costly, but the costly part can be avoided by copying results from another of the same type of object.
A deep copy makes an explicit, deep copy of all objects that make up the composition object. It can also be referred to as a recursive copy because all sub-objects are also copied. A shallow copy simply uses the existing references of sub-objects and does not copy them. This means that there are objects shared between the original and the copy.
State
Quite the opposite is true. This pattern is very useful when an object only has a few states. Objects with lots of states might actually result in very confusing code because of the large number of sub-classes. In this case it might be useful to model the changes differently --- especially since state changes might be costly and if there are a lot of states then it is likely changes will occur often. With only a few states the transitions are clear and behavior is logical. The TCP/IP connection example from the book is quite illustrative of this point.
Visitor
It seems that if the data to be "visited" by a Visitor is already accessible through the interface, then no changes need to occur to existing code. In general, this means that it is important for data intended to be acted on by a Visitor to have good accessibility.
Flyweight
In a way, subroutines in compiled code are flyweights. Rather than in-lining the same sequence of instructions in many places, a common set of object code is built and other pieces of code reference it remotely.
There is no minimum configuration. The only requirement is that some type of common data be shared explicitly rather copies be made.
Interpreter
An XML viewer/editor is an example where a well formed stream of data is parsed, but the output is in displayed and made interactive. Multimedia file formats might be handled similarly. They often contain a header and then some sequence of pages, frames, etc.. There may be sound frames inter-mixed with visual data. All this needs to be parsed and made viewable.