Introduction to Design Patterns in Java
The object-oriented approach tends to break down into simpler applications
and reusable components. However, this approach can quickly become a trap when
the cutting is done without clear rules. The designer came to be saturated by
the complexity of coding (spaghetti effect). Without basic architecture, your
application will gradually become unmanageable with the consequent emergence of
bugs more difficult to correct (domino effect). To reduce the risk of
maintenance, we must use an extra level in the object-oriented design: Design
patterns . But beware of the trap of the opposite extreme, these models are
common and so useful but only if the use is really necessary.
Some reminders about the design object
Unlike procedural languages such as C or Pascal, the object-oriented design
does not divide the space data (attributes) and space treatments (methods). This
common space applies to a part of the problem to be managed in the form of a
class. A class is an abstract representation describing how to run objects.
Objects are built from this class at runtime through an instantiation process
(in the Java new operator). Each of the statements in a class can be limited in
scope (local or private scope, scope of group or package, scope of descent or
protected, global scope or public ).
A class can be combined with other classes to facilitate reuse. The most
common association is inheritance. Inheritance is used to specialize an existing
class (link generalization / specialization) by changing / adding new methods
and new data. This specialization leads to the construction of new classes (also
called subclasses). The concept of inheritance can be seen as a design element
"is a kind of". For example, a car can be seen as a subclass of the Vehicle
class, car inherits the Vehicle class, it extends the features of the vehicle by
additional data and methods (speed, engine .. .). When you read "extend" we must
be aware that there is enrichment and that all the possibilities of the children
is always at least equal to or greater than the ancestors.
When a method of a
class is redefined in a subclass, there is an overload. As we will see later
this option gives flexibility to the introduction of generic classes delegating
certain behavior to subclasses. This overload is also the basis of the principle
of polymorphism (by inheritance because there are several kinds). Polymorphism
is a way to standardize access between different objects (type) and based on
common access (the common ancestor classes).
Some classes are not completely built to require subclasses to perform
overload. This is known as abstract or interfaces (fully abstract classes)
classes. The interface is used in Java and in recent languages objects, it is
a little more subtle than abstract class in a sense that his character
completely abstract avoids collision problems in inheritance between several
parents.
Interface is associated with one or more implementation classes . An
implementation class therefore contains the code for producing interface.
Interface is the "intent" and implementing the "realization".
In the following article, we will propose examples in Java. These examples
are easily transposed to C + + or other object language.
Definition of design
patterns
Design patterns describe practices organizations of object classes. These
organizations are often the result of an empirical design, the designer object
tries to facilitate the reuse and maintenance of the code. We can therefore
develop an application model as a form of organization transposed into other
applications. These systems may appear unnecessary or being complex to
beginners, yet it is very important to know more and apply it consistently (in
cases recognized as subject to change). The architect object is constructed
gradually a "basket" of models.
Design patterns are not really standardized, but you can cut them into three
broad categories:
- The creation models: These models are very common to
delegate to other classes the construction of other objects.
- The structure models: These models tend to develop centers
of classes with macro-components.
- Role models: These models attempt to divide
responsibilities between each class (use is dynamic)
If we wanted to draw a parallel with UML, the first two models are linked to
static diagrams (class), while the latter model is more related to a dynamic
pattern (sequence).
Models creation
We often faced the problem of classes evolution in object-oriented
programming. A class inherits from another class to specialize in certain items.
We would therefore like an object to belong to a particular class (in the same
family by inheritance) without having to search the management class of these
objects and the line of code that performs the instantiation. If we imagine a
case of creating an object for a given class C, distributed in different parts
of the code, if you decide to change the nature of C through a descendant class
(a class C 'inheriting C) it is necessary to retake the entire creation code
with a loaded class creation, and need only the latter is to renew.
-Factory
setting
A factory is a class that has the only role to build objects. This class uses
interfaces or abstract classes to hide the origin of objects.
Example1:
/ ** Interface description of a point * /
public interface Point 1{
/ ** Returns the abscisse of the point * /
public int getX ();
/ ** Returns the ordinate of the point * /
public int getY (); }
/ ** Interface description of a line * /
public interface Line1 {
/ ** Returns the coordinates of the first point * /
public int getX1 ();
public int getY1 ();
/ ** Returns the coordinates of the
second point * /
public int getX2 ();
public int getY2 ();
}
/ ** Factory returning objects of type point or line * /
public class
CanvasFactory1{
/ ** Returns a point at coordinates x, y * /
public Point GetPoint1 (int x, int y) {
return new
PointImpl1 (x, y);
}
/ ** Returns a line
with coordinates x1, y1, x2, y2 * /
public Line getLine1 (int
x1, int y1, int x2, int y2) {
return new LineImpl1 (x1,
y1, x2, y2);
}
}
In this example, we define two interfaces Point1
and Line1 representing two Abstract classes. These classes Point1 and Line1
denote objects returned by CanvasFactory1 class. This class hides the true
nature of objects. Here we return by the methods of access PointImpl1 and
LineImpl1 objects that implement respectively Point1 and Line1 interfaces. The
application will use the CanvasFactory1 class to obtain graphics elements.
During an evolution, a user may change easily the nature of objects (with other
classes that implement interfaces ... Point and Line).
Sometimes, we have two forms of factories:
- The abstract factories such as the one we have seen based on the use of
generic classes (Point and Line).
- The concrete factories masking all the necessary methods to the creation
and initialization of objects.
-Singleton
A singleton is used to control the number of instances of a class at any
given time. It is often very convenient for classes without condition and still
providing the same treatment.
A singleton is built with class methods. These
methods belong to a class and can be accessed independently of the object.
Example2:
Public CanvasFactory1 {
/ ** Data class containing the
current instance * /
private static CanvasFactory1 instance =
new CanvasFactory1 ();
/ ** Private Constructor prohibiting instantiation outside of
this class * /
private CanvasFactory1 () {}
/ ** Singleton of the current class * /
public static CanvasFactory1 getInstance () {return instance;}
...
}
To continue with the
previous example, we add to our CanvasFactory1 class a class attribute with the
declaration "private static" representing the available single instance of the
class. We add a constructor of private scope "CanvasFactory1 ()" to prevent
instantiation of this class. Finally the method "public static getInstance
CanvasFactory1" returns to us single instance of the CanvasFactory1 class.
Typically the use of CanvasFactory class will be as follows:
CanvasFactory1 cf = CanvasFactory1.getInstance ();
The singleton limit the number of instances in memory. It can sometimes be
seen as a particular factory.
-Builder
The Builder or editor is a class offering ways to build an object. For
example, to build a picture, we must add points, lines, circles.... It should
not be confused with the factory.
The problem with a factory setting, is that it
does not allows you to define how an object will be built, of course, it is
always possible to pass x parameters in the method of creating of a factory, but
this is often very reductive for the maintenance.
Example3:
/ ** Builder for Canvas objects * /
public
CanvasBuilder1 {
/ ** Initialize the canvas * /
public void initCanvas1 (Canvas1 e) {... }
/ ** Add a
point with coordinates x, y * /
public void addPoint1
(int x, y) {... }
/ ** Add the line coordinates x1, y1,
x2, y2 * /
public void addLine1 (int x1, int y1, int x2,
int y2) {... }
}
This example illustrates the use of the editor to build a drawing area
(canvas) with primitive. Firstly, we will call the method "initCanvas1" who will
designate a factory for a canvas and erase the drawing area. This canvas can be
enriched by the methods "addPoint1" or "addLine1". The user therefore does not
manipulate an object more directly, it applies a set of operations very
naturally. The designer of the Editor may at any time change the nature of the
object or the interpretation of the available methods.
Example for use:
/ / Create the Builder
CanvasBuilder1 cb = new
CanvasBuilder1 ();
Canvas1 e = new CanvasImpl 1();
/ / Mounting Canvas
cb.initCanvas1 (e);
cv.addPoint1 (5, 5);
cv.addLine1 (0, 0);
cv.addLine1 (10.0);
cv.addLine1 (10, 10);
We can exploit editors in addition to a factory. A factory
then uses the Builder to "climb" the returned object.
The structure models
These design models attempt to compose classes in order to build new
structures. These structures are not used primarily to manage differently groups
of objects and unique objects. Everyone using a vector drawing software is
required to group objects.
Objects designed in that way form a new object that
can be moved and manipulated without having to repeat these steps on each object
that compose it. This results in a wider structure but still easy to handle
structure.
-Adapter
The adapter is a convenient way to run an object with an interface that it
does not have. The idea is to create an additional class which is responsible
for implementing the right interface (the adapter) and call the corresponding
methods in the object to use .
Example4:
/ ** Interface representation of a circle * /
public interface Circle1{
/
** Returns the abscisse of the center of the circle * /
public int getX ();
/ ** Returns the ordinate of the center of the circle * /
public int getY
();
/ ** Returns the radius of the circle * /
public int GETZ ();
}
/ ** Class implementing the Circle interface * /
public class CircleImpl1
implements Circle1{
...
}
/ ** Adapter to convert the circle to a point * /
public class
CircleImplPointAdapter1 implements Point1
private Circle1 e;
public
CircleImplPointAdapter1 (Circle1 e) {
this.e = e;
}
public int getX ()
{return e.getX ();}
public int getY () {return e.getY ();}
}
This example attempts to convert an object implementing the interface Circle1
to an object of type Point1. A class CircleImplPointAdapter1 will make this
connection by charging itself to implement the point1 interface and call the
circle1 methods similar to the Point1 , ie getX and getY.
The adapter is used to link independent classes which does not have the right
interfaces. This operation is reserved for the special case where the change of
a parent class would be impossible or too complex. However, be careful not to
recklessly increase the number of class by introducing adapter at all levels.
The adapter is a way to make "connections".
-Proxy
Close enough to the adapter, the proxy looks to add a level of indirection
between the call of a method of an object and the associated action. To this
end, we construct a new class that implements the interface of the object to be
manipulated and deporting all actions on another object that implements the same
interface. This type of structure is to be able to change the target object
without changing the source object.
Example5:
/ ** Interface representing a drawing area * /
public interface Canvas1{
/
** Add a graphic object * /
public void addGraphicElement 1(GraphicElement1
ge);
}
/ ** Proxy for drawing area * /
public class CanvasProxy1 implements Canvas
1{
private Canvas1 e;
public CanvasProxy1 (Canvas1 e) {
this.e = e;
}
public void addGraphicElement1 (GraphicElement1 ge) {
e.addGraphicElement1
(ge);
}
}
In this example, a Canvas1 interface is defined with a method to add
graphical objects. The CanvasProxy1 proxy is a class that is responsible for
implementing the Canvas1 interface and to call methods on the Canvas object
passed as a constructor parameter. As a practical example, we can imagine that
the canvas can also exist in its opposite form to act as a gum. Passing through
mode "gum", any object added will erase some of the background as a footprint.
For this, it will be enough in CanvasProxy1 class to modify or change the
Canvas1 object. The operator will manipulate in all cases the same object.
Proxy are widely used for managing distributed objects (RMI in Java for
example). The idea is to build Proxy able to communicate with remote objects
(use of serialization in Java) without that the operator makes the difference
between a local access or remote access.
-Composite
Composite model seeks to eliminate differences between a group of objects and
an object. The simplest example is the group of objects in a drawing program as
we saw in the earlier example. The easiest way to manage these models is the use
of a single interface for single elements and the "Compound" element.
Example6:
/ ** Implementation of the interface * Composite Canvas /
public class
CanvasImpl1 implements GraphicElement1, Canvas1 {
/ ** Add a graphic element or
a canvas! * /
public void addGraphicElement 1(GraphicElement1 ge) {... }
Our example is based on the previous example, here we adopt two interfaces
for CanvasImpl 1 class:
- The Canvas1 interface containing the method "addGraphicElement1" to
build our drawing space.
- GraphicElement1 interface to associate the drawing area to a new graphic
element. It is therefore possible to add an object of the class CanvasImpl1
in an object of the CanvasImpl1 class.
The use of a common interface limit the need of test of each object, here all
the objects are seen in the same manner. This operation is encountered in the
DOM tree with XML documents.
Behavioral patterns
Behavioral patterns simplifies the runtime objects organization. Typically, a
function consists of a set of actions that may belong to different areas of the
implementation class. So we would be able to "delegate" some treatment to other
classes. In general, a model of behavior reduces the complexity of managing an
object or a set of objects.
- Iterator
The iterator is the most common behavior patterns. The idea is to limit the
vision of a collection by a user. Typically a collection contains a set of
objects stored by different methods (a table, a vector ...), the operator who
accesses the contents of the collection does not wish to be involved in this way
of managing objects. The collection provides a single point of access in the
form of an iterator interface.
Example7:
/ ** Class Management drawing space * /
public class CanvasImpl 1
implements GraphicsElement1, Canvas1 {
/ / Array to store
elements of the collection
private GraphicsElement1 [] ge;
...
/ ** Returns an iterator to access the objects in the
collection * /
public Iterator1 getIterator () {return ArrayIterator (ge);}
}
/ ** Interface for all collections of objects GraphicElement * /
public
interface Iterator1 {
public GraphicElement1 getNextElement ();
}
/ ** Iterator browsing a table to return objects of type GraphicElement * /
public class ArrayIterator implements Iterator1 {
private GraphicElement 1[] ge;
private int nge;
/ **
Constructor with a data table to run through) {
this.ge
= ge;
nge = 0;
}
/
** Returns each element of the collection or null * /
public
GraphicElement1 getNextElement () {
if (nge> = ge.length)
return null;
return ge [nge + +];
}
}
This example includes an iterator for CanvasImpl1 collection which contains
objects of type GraphicElement1. These objects are stored in an array, an
iterator ArrayIterator runs the table and offer each item in the collection by
getNextElement method. If later you want to change the method of storing objects
for reasons of performance or memory costs, simply make a new class that
implements the Iterator1 interface.
Conclusion
Design Patterns represent a very rich composition space or simplification of
your development object. We studied a few here, but there are many others and
you will also be asked to find new ones. Be careful not to get "carried away" by
these patterns, too many patterns is an "anti-pattern"; beautiful architecture
is always a balance between what is possible and necessary. Good design!
Programming
Introduction to Java EE (Part 1)
How the Google App Engine Works
WSDL - Web Service Description Language
SOAP: Simple Object Access Protocol
Initiation to Cryptography and Encryption to Secure Data
Introduction to Design Patterns in Java
How To Write Efficient Programs in Java
Proper Management of Logs in .Net
How To Remove FaceBook Action Ids From URL |