Answers to Design Pattern Questions
Abstract Factory |
Adapter |
Composite |
Decorator |
Facade |
Flyweight |
Factory Method |
Prototype |
Proxy |
Singleton |
Strategy |
Template Method
- How does this promote loosely coupled code?
The factory provides a centralized place to put decision code for
selecting between sub classed objects. This means that to add
another sub classed object the only work is in creating the new object
and adding a few lines of decision code to the factory. This is
especially great in highly modularized systems, in which,
modules may be added and removed from the system at any point.
- In the Implementation section of this pattern, the authors
discuss the idea of defining extensible factories. Since an Abstract
Factory is composed of Factory Methods, and each Factory Method has
only one signature, does this mean that the Factory Method can only
create an object in one way?
In the normal design of an abstract factory this is the case, however,
it would be possible to pass a parameter to a factory method and
return one of many sub classed objects based on that parameter.
- Consider the MazeFactory example. The MazeFactory contains a
method called MakeRoom, which takes as a parameter one integer,
representing a room number. What happens if you would also like to
specify the room's color & size? Would this mean that you would need
to create a new Factory Method for your MazeFactory, allowing you to
pass in room number, color and size to a second MakeRoom method?
This is definitely one possibility. Another would be to modify
the existing MakeRoom method with default values for color & size.
However, these may not provide the desired flexibility. See
below for more ideas.
- Of course, nothing would prevent you from setting the color and
size of the Room object after it has been instantiated, but this could
also clutter your code, especially if you are creating and configuring
many objects. How could you retain the MazeFactory and keep only one
MakeRoom method but also accommodate different numbers of parameters
used by MakeRoom to both create and configure Room objects?
One way to handle this would be to create a single MakeRoom that
takes two parameters. The first parameter is the room number
since that seems to be an absolutely required and another that is just
a list of zero or more key / value pairs that MakeRoom uses to
determine other room settings such as size and color.
- How does the Composite pattern help to consolidate system-wide
conditional logic?
The Composite pattern makes leaf and composite objects look the
same to the client. This keeps the client from having to
identify the object before it uses it. The result is a
centralized design that is much easier to maintain when the design
changes.
- Would you use the composite pattern if you did not have a
part-whole hierarchy? In other words, if only a few objects have
children and almost everything else in your collection is a leaf (a
leaf that has no children), would you still use the composite pattern
to model these objects?
This design pattern would certainly be a candidate for use
in this case. Even though most of the objects are leaves, those
leaves can be part referenced and organized by a composite object.
In fact, this is the way most composite designs are implemented, with
one composite object grouping multiple types of leaf objects.
- If a Proxy is used to instantiate
an object only when it is absolutely needed, does the Proxy simplify
code?
Proxy doesn't necessarily simplify code. The Proxy is used to
create an object when needed, allowing the application to run up until
this point unimpeded by the burden of loading the proxy object, which
likely encapsulates large amounts of data or an action that will take
a long period of time to execute.
- When should this creational pattern
be used over the other creational patterns?
The Prototype should be used when multiple similar objects are needed
that differ only in the state of their internal data. This
allows for creating complex objects, saving them, and reusing them.
Moreover, multiple complex objects can be created and saved from the
base object.
- Explain the difference between deep
vs. shallow copy.
A deep copy, copies the instance variables of the cloned object into
the prototype. The shallow copy just copies references to the
instance variables, allowing the two object to share the variables.
- The Singleton pattern is often
paired with the Abstract Factory pattern. What other creational or
non-creational patterns would you use with the Singleton pattern?
Factory - The factory just provides the logic for creating
other objects. It would make sense to only have one around if
for no other reason than to conserve memory.
Adapter - When the Adapter wraps a shared resource, an Adapter
Singleton can be used to ensure that everyone is using that resource
properly.
Builder - Similar to Adapter there may be a shared resource
that the Builder uses and it needs to manage accesses to that
resource.
- In the Implementation section of
the Decorator Pattern, the authors write: A decorator object's
interface must conform to the interface of the component it decorates.
Now consider an object A, that is decorated with an object B. Since
object B "decorates" object A, object B shares an interface with
object A. If some client is then passed an instance of this decorated
object, and that method attempts to call a method in B that is not
part of A's interface, does this mean that the object is no longer a
Decorator, in the strict sense of the pattern? Furthermore, why is it
important that a decorator object's interface conforms to the
interface of the component it decorates?
Object B is still a Decorator, in fact, this is the whole purpose of
the Decorator. Object B is providing additional functionality on
top of the object it decorates. The ScrollDecorator and
BorderDecorator examples in the book exhibit this quite well.
The ScrollDecorator provides a ScrollTo method and the BorderDecorator
provides a DrawBorder method. Plus, both provide a Draw method.
The Draw method is the shared interface while the others provide the
additional functionality.
The shared interface is important because it provides transparency to
the client which shouldn't notice if the Decorator isn't there.
- What is a non-GUI example of a
flyweight?
In a Voting System there would be hundreds of BallotItem Objects, of
which each Voter needs some subset. However, for each Voter the
only unique information is the result of the Voter's Vote. Thus,
a Flyweight could be used to provide access to the BallotItems without
creating new ones for each Voter.
- What is the minimum configuration
for using flyweight? Do you need to be working with thousands of
objects, hundreds, tens?
There is no set limit. If only two very large objects were
needed in the system and they differed very little, the Flyweight
would be perfect for aggregating their similarities and providing
access to different data for each. The Flyweight is even more
useful with large numbers of similar objects as it vastly reduces the
memory usage of the program.
- How complex must a sub-system be in
order to justify using a facade?
The sub-system does not have to be very complex. The Facade can
be used to provide a simplified interface to the underlying sub-system
or perhaps a small subset of the sub-system. Facades definitely
earn their keep in large, cumbersome subsystems, but they can provide
bountiful advantages to even small systems.
- What are the additional uses of a
facade with respect to an organization of designers and developers
with varying abilities? What are the political ramifications?
The Facade is best at hiding
the gory details of complex subsystems. An organization can use
this to its advantage by hiring highly skilled developers to build a
"dumbed-down" Facade for the lesser skilled developers to use.
This can make for rapid development, makes for a good design, and
makes good economic sense (i.e. not everyone is a high-priced expert).
However, this can lead to a "we're better than they are" environment,
which leads to poor morale. Poor morale is one of the biggest
killers for any development effort.
- Would you ever create an Adapter
that has the same interface as the object which it adapts? Would your
Adapter then be a Proxy?
Creating an Adapter that has the same interface would be perfectly
acceptable. This would allow for the flexibility of changing out
the adapted object at any time. The interface would still be the
same for the rest of the application, but the new adapter allows the
new object to work just like the old one. Moreover, the "same
interface" may be a well known or well documented interface to a given
object. The developers may be very accustomed to developing to
this interface. However, a new object/technology comes along and
the Adapter provides the same interface as has always been there.
This could be considered a Proxy, but Proxies are usually used to hold
the place of an object until is needed at execution time and then the
Proxy loads the needed data / objects.
- What happens when a system has an explosion of strategy objects? Is
there some better way to manage these strategies?
The best way to handle this is to remove all state from the strategies and turn them into Flyweights or Singletons.
The Flyweight would work if there was some small amount of state that the Strategy needed to maintain.
However, if there is no state and the Strategy just does processing, then a Singleton would suffice.
- In the implementation section of this pattern, the authors describe
two ways in which a strategy can get the information it needs to do its job. One way describes how a strategy
object could get passed a reference to the context object, thereby giving it access to context data. But
is it possible that the data required by the strategy will not be available from the context's interface?
How could you remedy this potential problem?
One way that the book lays out is to just pass in all the data that the context has to the Strategy. However,
this has the problem of possibly feeding more data than necessary to the Strategy, plus it may be an inefficient
method of getting the data to the Strategy. Another way is to make the Context a Template that takes Strategies
as parameters. This gives the Strategy finer grained access to the inner workings of the Context.
- The Template Method relies on inheritance. Would it be
possible to get the same functionality of a Template Method, using object composition?
What would some of the tradeoffs be?
Yes object composition could be used to implement the Template Method. Both rely on child
classes to perform key operations. However, the Composite is designed to build objects up out of
primitive objects and other Composite objects, which points out the key difference between the two.
The Template is designed to handle abstracted algorithms or actions while the composite is designed
to build up more complex objects. Using the Composite for the Template Method may be confusing
to the implementer if the algorithm is not "composed" of many primitive and composite algorithm
components.
Still To Come
Chain of Responsibility
Memento
Mediator
|