software patterns - l-università ta'...
TRANSCRIPT
SOFTWARE
PATTERNSJoseph Bonello
MOTIVATION
Building software using new frameworks is more
complex
And expensive
There are many methodologies and frameworks to
help developers build enterprise application
MOTIVATION
The main problems are:
Changes in business logic
Technology updates
Maintenance
Building software is difficult
Building reusable software is even harder!
WHY PATTERNS?
We need a common, tried-and-tested way of building and
testing software
Especially in those areas where common problems recur
The aim is to make it easier to change and maintain software
Other aims
Developers adopt a common design principle
Don’t waste time “hacking” your way into a solution
Reference on structures that get the work done efficiently
PATTERNS AND ANTI-PATTERNS
A pattern is a general, re-usable solution to a
common problem in software design
Gamma, Erich; Richard Helm, Ralph Johnson, and John Vlissides
(1995). Design Patterns: Elements of Reusable Object-Oriented
Software. Addison-Wesley. ISBN 0-201-63361-2 (Gang-Of-Four
Book)
PATTERNS AND ANTI-PATTERNS
An anti-pattern is a commonly used pattern but is
counterproductive or ineffective in practice
Experienced OO designers, in general, do not try
to solve a problem from first principles
Instead, they prefer to reuse a solution that has worked in the past
WHAT CONSTITUTES A PATTERN?
A Pattern has 4 essential elements:
A pattern name: Used to refer to a description of a design
problem, its solutions and consequences using a descriptive alias.
The alias allows us to communicate with other and design at a
higher level of abstraction.
The problem: It describes when to apply the pattern. It describes
the context of the problem such as class/object structures
symptomatic of bad design or a list of conditions that must be met
before applying the pattern.
WHAT CONSTITUTES A PATTERN?
A Pattern has 4 essential elements:
The solution: describes the elements that make up the design, the
relationships, responsibilities and collaborations. It does not
describe a concrete implementation. It is an abstract description of
the general arrangement that will solve the problem.
The consequences: refer to the results and trade-offs or applying
the pattern. They are used to judge the costs and benefits of
applying the pattern. Consequences include impact on system
flexibility, extensibility and portability.
CATEGORIES OF PATTERNS
Creational patterns Deal with object creation mechanisms
Structural patterns Ease the design of defining relationships between entities
Behavioral patterns Used to identify communication patterns between objects and
increase flexibility when carrying out this communication
CREATIONAL PATTERNS
We shall be looking at the following Creational
Patterns
Singleton Pattern
Abstract Factory (Kit) Pattern
THE SINGLETON PATTERN IA
Provides a single object throughout the
lifetime of an application
Provides a single access point to the object
THE SINGLETON PATTERN IB
An example would be to have one database
connection per client application
Used when:
There must only be one instance of a class
Clients need one single way of accessing the instance
THE SINGLETON PATTERN II
Benefits:
Controlled access to sole instance (or multiple instances)
Avoids “global variables” in namespace
Permits Subclassing
More flexible than static member and methods
SINGLETON PATTERN III
public class ClassicSingleton {private static ClassicSingleton instance =
null;
protected ClassicSingleton() {// Exists only to defeat instantiation.
}
public static ClassicSingleton getInstance() {if(instance == null) {
instance = newClassicSingleton();
}return instance;
}}
SINGLETON PATTERN IVpublic class ThreadSafeSingleton {
private static ThreadSafeSingleton instance =
null;
protected ThreadSafeSingleton() {
// Exists only to defeat
instantiation.
}
public synchronized static
ThreadSafeSingleton getInstance() {
if(instance == null) {
instance = new
ThreadSafeSingleton();
}
return instance;
}
}
http://www.javaworld.com/article/2073352/core
-java/simply-singleton.html
ABSTRACT FACTORY (KIT) I
Provide an interface for creating families of related
or dependent object without specifying their
concrete classes
Used to de-couple clients from a particular
concrete implementation
Example: Different implementations of handling an order’s costs (e.g.
TaxCalculator(EU,USA, CN,etc), shipping costs, etc)
ABSTRACT FACTORY (KIT) IIA
ABSTRACT FACTORY (KIT) IIB
ABSTRACT FACTORY (KIT) III
Use the Abstract Factory pattern when
a system should be independent of how its products are
created, composed, and represented.
a system should be configured with one of multiple
families of products.
a family of related product objects is designed to be
used together, and you need to enforce this constraint.
you want to provide a class library of products, and you
want to reveal just their interfaces, not their
implementations.
ABSTRACT FACTORY (KIT) IV
Benefits:
Isolates concrete classes
Allows to change product family easily
Promotes consistency among products
Factory usually a Singleton; ideally create<Object> should have
a type parameter to make it extensible
ABSTRACT FACTORY (KIT) V
Step 1: The Interface
public interface Shape {
void draw();
}
Step 2: The Concrete Class
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
ABSTRACT FACTORY (KIT) V
Step 3: Create the Abstract Factory Class
public abstract class AbstractFactory {
abstract Shape getShape(String shape) ;
}
Step 4: Create the Concrete Factory Class
public class ShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType == null){ return null; }
if(shapeType.equalsIgnoreCase("RECTANGLE")){ return new Rectangle(); }
return null;
}
}
ABSTRACT FACTORY (KIT) V
Step 5: Create Factory Producer
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){ return new ShapeFactory(); }
return null;
}
}
ABSTRACT FACTORY (KIT) V
Step 6: Use Producer to Get Factories
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
//get shape factory
AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
// get an object of Shape Rectangle
Shape shape1 = shapeFactory.getShape(“RECTANGLE");
// call draw method of Shape Rectangle
shape1.draw();
}
}
STRUCTURAL PATTERNS
In this section, we’ll discuss the following patterns
Adapter (or Wrapper) Pattern
Bridge Pattern
Composite Pattern
Decorator Pattern
Façade Pattern
ADAPTER (WRAPPER) I
Convert the interface of a class into another interface
clients expect. Adapter lets classes work together that
couldn't otherwise because of incompatible interfaces.
Example
Merging a new library with an old library you discover two methods with
the same name but different parameters
ADAPTER (WRAPPER) II
Benefits:
Allow two or more incompatible objects to communicate and interact.
Improves reusability of older functionality.
ADAPTER (WRAPPER) III
Use when:
When you want to use an existing class, and its interface
does not match the interface you need.
When you want to create a reusable class that
cooperates with unrelated or unforeseen classes, classes
that don't necessarily have compatible interfaces.
ADAPTER (WRAPPER) III
When you want to use an object in an environment that
expects an interface that is different from the object's
interface.
When you must ensure interface translation among
multiple sources.
ADAPTER (WRAPPER) III
ADAPTER (WRAPPER) III
Step 1: Create the interfaces
public interface MediaPlayer {
public void play(String audioType, String fileName);
}
public interface AdvancedMediaPlayer {
public void playVlc(String fileName);
public void playMp4(String fileName);
}
ADAPTER (WRAPPER) III
Step 2: Create the concrete class implementing the advanced interface
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName); }
@Override
public void playMp4(String fileName) { //do nothing }
}
ADAPTER (WRAPPER) III
Step 2: Create the concrete class implementing the advanced interface
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName); }
@Override
public void playMp4(String fileName) { //do nothing }
}
ADAPTER (WRAPPER) IIIStep 3: Create the adapter for MediaPlayer
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
}
else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
@Override public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName); }
else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName); }
}
}
ADAPTER (WRAPPER) IIIStep 4: Create the concrete implementation of the adapter
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
//inbuilt support to play mp3 music files
if(audioType.equalsIgnoreCase("mp3")){ System.out.println("Playing mp3 file. Name: " +
fileName); }
//mediaAdapter is providing support to play other file formats
else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
}
else{ System.out.println("Invalid media. " + audioType + " format not supported"); }
}
}
ADAPTER (WRAPPER) III
Step 5: Use the adapter
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
}
}
BRIDGE (HANDLE) PATTERN I
Used to decouple an abstraction from its implementation so that the
two can vary independently
When an abstraction (abstract class) can have several implementations,
the usual way to accommodate them is to use inheritance
This isn’t always a flexible approach because the implementation binds to the
abstraction permanently
BRIDGE (HANDLE) PATTERN I
Use the pattern when:
You want to avoid a permanent binding between an abstraction and
its implementation
Both the abstractions and the implementations should be extensible
by sub-classing
Changes in the implementation of an abstraction should not impact
clients
BRIDGE (HANDLE) PATTERN II
Use the pattern when (cont):
You have a class hierarchy that proliferates because it needs to adapt to various
specific implementations
You want to share an implementation among multiple objects but you want to keep
the fact hidden from the client.
BRIDGE (HANDLE) PATTERN III
Known uses
GUI frameworks as discussed previously.
Persistence Frameworks
Consequences:
Implementation is not bound permanently to an interface
Eliminates compile time dependencies (no recompilation of abstract class)
Decoupling encourages layering, therefore a better structured system
Improved extensibility
Hiding implementation details from clients
COMPOSITE PATTERN I
Used to compose objects into tree structures to represent part-whole
hierarchies.
Clients treat individual objects and compositions of objects uniformly
Example
Consider graphics applications that allow users to build complex diagrams out of
simple components which can be grouped into more complex ones
A simple implementation would define classes for graphical primitives and other
classes that act as containers for these primitives
Problem: code using these classes must treat primitives and objects differently
The distinction increases the complexity of the system
The pattern uses recursive composition so clients do not make this distinction
COMPOSITE PATTERN II
Use the pattern when:
You want to represent part-whole hierarchies of objects
You want clients to be able to ignore differences between compositions of objects
and individual objects
COMPOSITE PATTERN III
Example
Consequences
Define class hierarchies consisting of primitive objects and composite objects
Simplifies the client’s architecture
Simplifies the process of adding new components
The design can be overly general (disadvantage)
DECORATOR (WRAPPER) PATTERN I
Decorator is used to attach responsibilities to an object dynamically
Decorators provide a flexible alternative to sub-classing for extending
functionality
Why use it?
We use it when we need to add responsibilities to individual objects, not the entire
class (e.g. adding borders or scrollbars to a visual widget)
If you use inheritance will affect every instance which will not allow it to vary the
choice as it is statically linked
The solution is to add the object, called the decorator or wrapper, within another
that adds the required property
DECORATOR (WRAPPER) PATTERN II
Use the Decorator
To add responsibilities to individual objects dynamically and transparently
To withdraw responsibilities from the object
When extending by sub-classing is impractical or not permitted
A large number of independent extensions would result in an explosion of classes
A class definition may be hidden or sealed (final)
DECORATOR (WRAPPER) PATTERN III
Consequences
More flexible than static inheritance
Easier to add a property twice (e.g. a widget with a double border)
Avoids feature-laden classes up in the hierarchy, reducing complexity. Features are
added incrementally with new decorator objects
The decorator and its component are identical, the decorator is a transparent
enclosure similar to a photograph frame
Lots of little objects (disadvantage), difficult to learn and debug
Used frequently in UI Toolkits and in the implementation of IO stream
classes
FAÇADE PATTERN I
Provides a unified interface to a set of interfaces in a subsystem
Defines a higher level interface that makes the subsystem easier to use
Structuring a system into subsystems helps reduce complexity
Common design goal is to minimise communication and dependency
between subsystems
Façade objects provides a single, simplified interface to more general
facilities of the sub-system
FAÇADE PATTERN II
Example: Programming environment providing access to its compiler
subsystem
Higher level interface shields clients from intricate details of different
parts of compiler by allowing access to specific functionality of different
subparts
Use Façade when:
Provide a simple interface to a complex system
There are many dependencies between clients and the implementation classes of an
abstraction – Façade decouples the sub-system from clients
Layer your subsystems. Façade defines the entry to each subsystem layer
FAÇADE PATTERN III
Consequences
Shields clients from sub-system components
Promotes weak coupling between sub-system and its clients
Reduces compilation dependencies
Does not prevent applications from using subsystem classes if they need to
BEHAVIOURAL PATTERNS
These are some common behavioural patterns:
Iterator Pattern
Observer Pattern
Strategy Pattern
ITERATOR (CURSOR) PATTERN I
Provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation
An aggregate object (e.g. List) should give you a way to access its elements without exposing its structure
You might want to traverse the list in many ways, but you want to keep the design and implementation of the List clean
The idea of the pattern is to take the responsibility for accessing and traversing the list using at iterator object
The iterator keeps track of the current element
The List is responsible for creating its own iterator, possibly using a Factory to generalise the operation
ITERATOR (CURSOR) PATTERN II
Use this pattern when you want
To access an aggregate object’s contents without exposing its internal representation
Support multiple traversals of aggregate objects
Provide a uniform interface for traversing different aggregate structures
ITERATOR (CURSOR) PATTERN III
Consequences of using this pattern
Supports variations in the traversal of an aggregate
Simplify the aggregate interface
Mode than one traversal can be pending on the same aggregate
Known uses: Collection classes (Lists, Vectors, etc)
OBSERVER (DEPENDENTS) PATTERN I
Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically
When partitioning a system into a collection of cooperating classes, one needs to maintain consistency between related objects
Note that tightly coupling class is not a solution because it reduces reusability
The observer pattern describes how to establish common relationships between objects
The key objects are the subject and the observer
All observer are notified when the subject changes (publish-subscribe model)
OBSERVER (DEPENDENTS) PATTERN II
Used when
Abstraction has two aspects, one dependent on the other. Encapsulate the objects
separately and reuse them independently
When a change to one object requires changing the others, and you do not know
how many objects to change
When an object needs to be able to notify other objects without making
assumptions about who these objects are (not tightly coupled)
OBSERVER (DEPENDENTS) PATTERN III
Consequences
Abstract coupling between Subject and Observer. Subject knows that it has a list of
observers conforming to the observer interface, it does not know the concrete class
of the observer – minimal coupling
Support for broadcast communication
Unexpected updated (disadvantage). Since observers have no knowledge of other
observers, changing the subject might result in undesired updates
STRATEGY (POLICY) PATTERN I
Define a family of algorithms that encapsulate one another and make
them interchangeable
Strategy lets the algorithm vary independently of the client that uses
them
Example: Many algorithms exist for breaking a stream of text into lines.
Hard-wiring all algorithms into the classes that require them is not
desirable because
Client get overly complex
Different algorithms will be appropriate at different times
Difficult to add new algorithms and vary existing ones
STRATEGY (POLICY) PATTERN II
Use this pattern when
Many related classes differ only in their behaviour
Need different variants of an algorithm
An algorithm uses data that clients shouldn’t know about (avoid exposing complex,
algorithm-specific data structures)
A class defines many behaviours that appear as multiple conditional statements in its
operators
STRATEGY (POLICY) PATTERN III
Consequences
Families of related algorithms. Hierarchies of Strategy classes define a family of
algorithms that can be reused
An alternative to sub-classing, which is easier to switch to, understand and extend
Strategies eliminate conditional statements
Provides a choice of implementations
Clients must be aware of different strategies (disadvantage)
Communication overhead between Strategy and Context (Strategy interface is
shared, so some simple concrete classes may use little or none of the parameters
passes to them. Tightly couple context and strategy to solve this problem)
Increased number of objects
OTHER
Model-View-Controller (MVC)
This pattern isolates domain logic from the user interface
The model manages the behaviour and data of the application domain,
The view renders the model into a form suitable for interaction, typically a user
interface element.
The controller receives input and initiates a response by making calls on model
objects. A controller accepts input from the user and instructs the model and
viewport to perform actions based on that input.
Sometimes considered a framework
FURTHER READING
Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (2007). Design Patterns:
Elements of Reusable Object-Oriented Software. USA: Addison-Wesley
Professional (ISBN 0-201-63361-2)
McConell, S. (2004). Code Complete: A Practical Handbook of Software
Construction. USA: MICROSOFT PRESS (ISBN 0-735-61967-0)
Alur, D., Malks, D., & Crupi, J. (2003). Core J2EE Patterns: Best Practices and
Design Strategies. USA: Prentice Hall (ISBN 0-131-42246-4)
http://www.dofactory.com/Patterns/Patterns.aspx
http://www.oodesign.com