Design Patterns...

S

secret

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.

Here are some of the patterns to jog your memory...

Creational Patterns...
Abstract Factory,
Builder,
Factory Method,
Prototype,
Singleton

Structural Patterns
Adapter,
Bridge
Composite
Decorator
Facade
Flyweight
Proxy

Behavioural Patterns
Chain of Responsibility
Command
Interpreter
Mediator
Memento
Observer
State
Strategy
Template Method
Visitor
State Machine

Some of them are more self-explanatory than others, but real world examples
would be appreciated in any of them...

alan
 
J

John C. Bollinger

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.

Here are some of the patterns to jog your memory...

Creational Patterns...
Abstract Factory,
Builder,
Factory Method,
Prototype,

Prototype (with Factory Method):

In a DNS client application, there is a class hierarchy for the various
types of DNS Resource Records. When constructing an object representing
a DNS response, it is necessary to obtain Resource Record objects of the
appropriate classes, based on the DNS resource record type and class codes.

The chosen solution was to give the ResourceRecord base class a
newInstance() method that creates and returns (as it says) a new
instance of ResourceRecord based on the parameters specific to a
particular raw record. Subclasses override this method appropriately,
providing instances of their own class. To construct the resource
record object the client looks up the class / type combination in a Map,
obtaining from it the appropriate prototype object, and then invokes the
newInstance method of the prototype. (And this is all performed inside
a Factory object, an implementation of one of the patterns you didn't
mention.)

Advantages:

(1) User can easily configure classes for new resource record types, or
alternative classes for supported resource record types.

(2) The client code is decoupled from specific resource record
implementations.

(3) No use of reflection.
Singleton

Singleton:

[Comment: this one is overused in my experience, perhaps because it is
fairly easy to understand. This pattern should be applied only when it
is _necessary_ that there be only one instance.]

As a somewhat abstract description of another real example, take a case
where a collection of objects in potentially many threads need to draw
unique numbers from a continuous sequence (serial numbers, for
instance). Suppose also that these disparate objects have no direct
knowledge of each other. In order that all possible objects that use
the same sequence, the sequence is encapsulated in a Singleton object.
When an object needs a number from the sequence, it obtains the (one)
instance of the Singleton and invokes the appropriate method on it.
Structural Patterns
Adapter,

Adapter:

A user must write tests for two different implementations of the same
basic functionality. Instead of two completely different test suites,
the user writes one suite and for each implementation one adapter class
so that the test suite can work with either one.
Bridge
Composite
Decorator

Decorator:

[From JUnit] a JUnit TestSuite instance defines one or more tests to be
run. For each one there is a setup step, a test step, and tear down
step; these are run sequentially. TestSuite implements an interface
Test, via which the group of tests is executed. If it is desired to
perform a setup and/or teardown procedure for the whole suite, then the
TestSuite can be decorated by a TestSetup that defines them; TestSetup
also implements Test and wraps a TestSuite.
Facade
Flyweight
Proxy

Behavioural Patterns
Chain of Responsibility
Command
Interpreter
Mediator
Memento
Observer
State
Strategy

Strategy:

[From Java Collections] The Comparator interface and all its
implementations. The Collections and Arrays classes provide generic
methods by which to sort objects; the specific sort order is directed by
a Comparator instance. Different Comparators give you potentially
different sort orders.
Template Method

Template Method:

[Also from Java Collections] The AbstractCollection class defines an
entire Collection implementation in terms of abstract iterator() and
size() methods, and a stub add(Object) method. These three (especially
the first two) are template methods -- a concrete subclass can override
just those and thereby fully implement Collection in any manner desired.

Other AbstractXXX classes among the Collection classes work similarly.
Visitor
State Machine


John Bollinger
(e-mail address removed)
 
S

secret

Thanks John, that's fantastic. Very helpful.

alan

John C. Bollinger said:
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.

Here are some of the patterns to jog your memory...

Creational Patterns...
Abstract Factory,
Builder,
Factory Method,
Prototype,

Prototype (with Factory Method):

In a DNS client application, there is a class hierarchy for the various
types of DNS Resource Records. When constructing an object representing
a DNS response, it is necessary to obtain Resource Record objects of the
appropriate classes, based on the DNS resource record type and class codes.

The chosen solution was to give the ResourceRecord base class a
newInstance() method that creates and returns (as it says) a new
instance of ResourceRecord based on the parameters specific to a
particular raw record. Subclasses override this method appropriately,
providing instances of their own class. To construct the resource
record object the client looks up the class / type combination in a Map,
obtaining from it the appropriate prototype object, and then invokes the
newInstance method of the prototype. (And this is all performed inside
a Factory object, an implementation of one of the patterns you didn't
mention.)

Advantages:

(1) User can easily configure classes for new resource record types, or
alternative classes for supported resource record types.

(2) The client code is decoupled from specific resource record
implementations.

(3) No use of reflection.
Singleton

Singleton:

[Comment: this one is overused in my experience, perhaps because it is
fairly easy to understand. This pattern should be applied only when it
is _necessary_ that there be only one instance.]

As a somewhat abstract description of another real example, take a case
where a collection of objects in potentially many threads need to draw
unique numbers from a continuous sequence (serial numbers, for
instance). Suppose also that these disparate objects have no direct
knowledge of each other. In order that all possible objects that use
the same sequence, the sequence is encapsulated in a Singleton object.
When an object needs a number from the sequence, it obtains the (one)
instance of the Singleton and invokes the appropriate method on it.
Structural Patterns
Adapter,

Adapter:

A user must write tests for two different implementations of the same
basic functionality. Instead of two completely different test suites,
the user writes one suite and for each implementation one adapter class
so that the test suite can work with either one.
Bridge
Composite
Decorator

Decorator:

[From JUnit] a JUnit TestSuite instance defines one or more tests to be
run. For each one there is a setup step, a test step, and tear down
step; these are run sequentially. TestSuite implements an interface
Test, via which the group of tests is executed. If it is desired to
perform a setup and/or teardown procedure for the whole suite, then the
TestSuite can be decorated by a TestSetup that defines them; TestSetup
also implements Test and wraps a TestSuite.
Facade
Flyweight
Proxy

Behavioural Patterns
Chain of Responsibility
Command
Interpreter
Mediator
Memento
Observer
State
Strategy

Strategy:

[From Java Collections] The Comparator interface and all its
implementations. The Collections and Arrays classes provide generic
methods by which to sort objects; the specific sort order is directed by
a Comparator instance. Different Comparators give you potentially
different sort orders.
Template Method

Template Method:

[Also from Java Collections] The AbstractCollection class defines an
entire Collection implementation in terms of abstract iterator() and
size() methods, and a stub add(Object) method. These three (especially
the first two) are template methods -- a concrete subclass can override
just those and thereby fully implement Collection in any manner desired.

Other AbstractXXX classes among the Collection classes work similarly.
Visitor
State Machine


John Bollinger
(e-mail address removed)
 
H

Harald Hein

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:
Abstract Factory,

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.
Factory Method,

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.
Prototype,

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;
}
}
Singleton

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.
Flyweight

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top