secret said:
I've been reading the GoF book and it clicks (at last) as I'm
reading it. However, I think I (and maybe many others) would
benefit greatly from hearing about some real world uses of the
patterns. What problems did they actually help solve.
Understanding the multitude of GUI examples in the book does not
help me to see applications in other areas and I fear that I
wouldn't recognize a relevant occasion to use some of them if I
came across it.
You don't need a pattern if you don't recognize an opportunity to use a
pattern. No joke! A common beginner's error is to look out for
opportunities to apply patterns and then plaster a whole application
with all kinds of patterns. Which just adds overhead and complexity.
A pattern give you a certain flexibility in your application. If you
don't need that flexibility, you are just adding overhead.
First you need to have a problem at hand! If you see one in your code,
you try to define the problem (something a lot of posters who post here
with brain-dead "help!!! XYZ does not work!!!" posts fail to manage at
all). Defining the problem includes listing the requiements for a
solution. Then you search for a solution satisfying the requirements.
While you do this, you also think about the patterns you know. Maybe
one fixes the problem. But, every pattern comes with a price. So you
also evaluate if you are willing to pay the price. If you are, go for
it. If not, continue to search.
The key to master patterns is not to apply them whenever possible. The
key is to stay away from them when not needed.
Some of them are more self-explanatory than others, but real world
examples would be appreciated in any of them...
Every example in the GoF book is a real-world example. In fact, every
pattern description ends with a list of known usages. But ok, here are
some more:
This is almost a meta-meta pattern. Instead of just having a factory
which shields object creation from the user, one also allows to
select/replace the factory itself. So you actually end up with a two-
step procedure:
1) Select factory at some point in time - maybe even hiden from you
2) Creat specific object from the range of objects the factory
can create
Sometimes, step 1) is also done using a factory. So you end up with a
three step-procedure starting with a factory creating a factory ...
If you need this pattern, you usually need it badly. Otherwise, stay
away from it.
Last time I used this when I had to incooperate communication with two
legacy systems into a new application. Both systems did the same thing,
but with a completely different communication protocol. So the first
step was to provide adapters for both systems to map the
communication with both of them to a unified interfaces. The rest of
the system only knew how to work with the unified interfaces.
The second step was to provide two separate factories that could
generate the adapter objects. Both factories implemented the same
interface. The interface served as the abstract factory (I didn' use an
abstract base class, because there was no implementation to share).
At the start of the system a simple factory method was used to select
the factory class, based on user input, user rights, pre-configurations
and availability of the external systems.
I had to create an application that was supposed to create a lot of
reports. Due to the amount of data it was not possible to store all
data neded for the reports until the end of the application. So
unfortunately, the reports had to be generated while the application
ran. And it had to support a lot of different report formats.
So the application got a reporting interface which dispatched the
intermediate results delivered to the interface to concrete builder
implementations for the different reporting formats. The Builders
generated the reports on the fly as soon as they had enough data for
their specific report format.
Usually used in conjunction with someting else. E.g the usage of the
flyweight pattern described below was done using a factory method. The
creation of a particular factory class in the abstract factory example
was also done using a factory method.
I sometimes see people using this pattern when they don't know it. E.g.
when people mimic copy constructors:
class MyClass {
/** Initialize new object from old object **/
public MyClass(MyClass other) {
this.someValue = other.someValue;
}
}
All it would take to make this an application of the prototype pattern
more obvious would be some renaming:
class MyClass {
/** Use a prototype to initialize new object **/
public MyClass(MyClass prototype) {
this.someValue = prototype.someValue;
}
}
This is nice, but not very poweful. So you e.g. end up with a factory
like this:
class MyFactory {
public setPrototype(MyClass prototype) {
this.prototype = prototype;
}
public creatMyClass() {
return new MyClass(this.prototype);
}
}
You coud do the same using cloning instead of this copy-constructor
fakeing.
I really don't remember when I used it this way last time. It just
happens. I do remember that I used something similar in a constructor,
where the default data for objects was read from a properties file.
Instead of reading the properties file every time when a new object of
that type was constructed I just read the data once and buffered it.
The buffer was a "half initialized" object. But that implementation
would more count as an application of the "first-time in" pattern:
class MyClass {
static MyClass default_ = null;
public MyClass() {
if(default_ == null) {
// first-time in, get default data
default_ = readDataFromPropertyFile();
}
this.someValue = default_.someValue;
this.anotherValue = default_.anotherValue;
}
}
Overused. As a beginner, stay away from it! If you ever implement it in
a thread-save way, DO NOT optimize it with he double-check lock
pattern. Double-check lock can't work reliable due to Java's memory
model,
See my abstract factory example. In order to integrate the legacy
systems, both interfaces to the systems need to be unified. This was
done with a set of adapters.
In a certain engineering program I needed thousands of objects to
represent the data (think of a simulation). Each object had a set of
attributes holding its configuration state and its specific data. Due
to the nature of the application usually almost all objects had the
same configuration state, which never changes during the application.
Instead of having the 25 or so different configuration state variables
in each object, each object only had one referenc to a configuration
state object from a pool of flyweights. The state objects were
immutable. When a new data object was created, the pool was queried for
a state object (using a factory method). If a matching one existed the
reference to the matching one was returned. If non existed, a new one
was created, added to the pool and returned.
In a planning application a lot of different calculations had to be
done on the data. And I knew from the beginning that once the
first calculations were implemented people would ask for more. So
instead of implementing the calculations with the data-holding objects,
they all got a visitor interface.
On top of that there was a simple "query engine". All this engine did
was to (a) find the right entry into the huge pile of data (based on
some start date, project name, etc.), (b) configured a visitor to do
the necessary navigation inside the data (which trails to follow, end
date, etc.), and (c) started the visitor on the first object. After
some time the visitor returned with the right result.
It was really fun to sit on top of this pile of data and just fire
visitor calculation requests into it.