1

SEG3100 LARGE SCALE SOFTWARE DEVELOPMENT: (Misbah Islam)

Lecture C2: DESIGN PATTERNS ( Updated Sept 28, 2004)

Why are they useful:

Patterns are useful because they

  • Point the way to solve a problem based on past best practices. It provides a general methodology and structure.
  • Allow you not to design the solution from the ground up. You can reuse the existing solutions.
  • Are easily adaptable to your requirements since it summarizes many solutions that have worked in the past.

How to use a Pattern:

Patterns can be assembled within the context of a high level language or through the use of a Pattern Language that the rules to combine them into an architectural style.

To use the pattern in software design one has to

  • Define what the problem is
  • Identify the context of the problem and its solution
  • Determine which particular solution is ‘best’

Levels of Patterns:

  • Architectural Patterns
  • Design Patterns
  • Language related Idioms

Architectural Patterns

An architectural pattern provides a structural arrangement for software systems. It includes subsystems along with their characteristics and the rules whereby subsystems can be connected to each other.

Design Patterns

A design pattern is a next-level pattern that can be used to define and design subsystems, packages and components. It provides pointers to commonly recurring problems, e.g. memory management, pointer management, synchronization and mutual exclusion, deadlock avoidance etc. They are described using UML type design constructs, for example objects, classes, inheritance, aggregation and uses, extends relationship.

Idioms

An idiom is a low-level pattern specific to a programming language. An idiom describes how to implement particular parts using a given language, e.g. memory allocation, de-allocation, critical regions, etc.

Example of a Design Pattern Use:

An organization needs to develop a generalized GUI generator. This GUI generator should be usable to design GUIs for Motif, Sun and Windows. Instead of having to develop widgets (buttons, menus, sliders, scroll bars etc from scratch, a developer can invoke the set of classes of the GUI generator that define the widgets.

The solution is to design a GUI generator in which the application program is uncoupled from the specific GUI. This can be achieved by having a design pattern called Abstract Factory, Figure 3.

refers

creates

Notes:

-The abstract class ‘Factory’ cannot be instantiated (no object be constructed using this)

-An abstract class usually has one abstract method
(can be made ‘pure’ so that it cannot be invoked)

-The abstract class can be used as a base class for other concrete classes, e.g. Motif.

-Concrete classes can invoke their methods to create actual widgets.

-The CLIENT only deals with abstract classes (shown in red). None of the interfaces are specific to any one GUI because the methods of the abstract class are also abstract. Thus the design has uncoupled the client application from the GUI.

How to create a widget:

-To create a widget, the client program need only send a message to method ‘create menu’ of Abstract Factory Widget. Polymorphism should ensure that the correct widget is created.

-First an object ‘GUI Factory’ of the class “Concrete Factory Motif’ is created.

-A message to abstract method ‘create menu’ of Abstract Factory Widget (passing ‘GUI Factory’ as parameter) is interpreted as a message to method ‘create menu’ within concrete subclass ‘Concrete Factory Motif’.

-Method ‘create menu’ will in turn send a message to create a Motif menu.

Families of Patterns:

  • Creational
  • Structural
  • Behavioural
  • Architectural

Examples Patterns:

Purpose / Pattern Name / Features
Creational / Abstract Factory / Context:
-Provides an interface for operations that create abstract product objects.
Solution:
-Concrete Factory subclass creates concrete objects
(See Figure 3 above)
Singleton / Context:
-Ensures that a class has one instance, e.g. one database per company.
Solution:
-There is a private constructor that ensures no other class is able to recreate another instance


Structural / Abstraction-Occurrence / Context:
-Airline flight with the number but leaving on different days, with different crews and passengers
Solution:
-Create an abstraction class containing common info
-Create an occurrence class (create 1:n association)
*

General Hierarchy / Context:
-Relationship between managers and employees.
Solution:
-Create an abstract Node class representing common features
-Create at least two subclasses, SuperiorNode, & Non-Superior Node. SuperiorNode with ‘subord’ association
-Composite pattern is a specialization of this pattern
* supervises



0,,1

Adapter / Context:
-You are building an inheritance hierarchy and want to reuse an existing class.
Solution:
-Create an adapter class and associate existing class to it, called ‘Adaptee’
-The polymorphic methods of Adapter can ‘delegate’ to methods of the Adaptee.
-Client calls operations on an Adapter instance, which then calls Adaptee’s operations to carry out the request






Façade / Context:
-Provides a unified I/F to a subsystem’s I/Fs.
Examples:
-Façade is Complier, subsystem classes, Scanner, Parser, Program node.
-Airline Façade. Subsystem classes, Flight, Person
*


*
Proxy / Context:
-Creating instances of a ‘heavy-weight’ e.g. database persistent objects can be time-consuming and expensive. Proxy provides a stand-in for a real thing till its creation cannot be avoided.
Solution:
-Client invokes the Proxy class, which presents the same interface as the heavy-weight object.




* *

Delegation / Context:
-Your class needs an operation, that has already been implemented by another class
Solution:
-Create a Delegator and Delegate classes and create association between them
-Create a method in Delegator class that calls the method in the Delegate class.


Behavioural / Observer / Context:
-Define a one-to-many relationship between objects, such that one object changes state, all its dependents are notified and updated automatically, e.g. changes in DB reflected to spread sheet, bar and pie charts.
Solution:
-Create an abstract class ‘Observable’ that maintains a collection of Observer instances Delegate classes and create association between them
* *




Player-Role / Context:
-One class needs to have several roles. This pattern reduces the need to have multiple inheritances.
Solution:
-Create a ‘Player’ class and an abstract ‘Role’ class that is a super class of a set of possible roles. Create an association between Player and to an abstract Role class. Role can also be an interface.






Immutable / Context:
-This type of class has a state that will never change once an object is created. Immutable instances.
Solution:
-Ensure that the state changes only take place in the constructor method and no where else.
Read-Only / Context:
-Related to Immutable except that certain privileged classes are allowed to change that state.
Solution:
-Create a ‘Mutable’ class that is not public. All classes that need to modify this class called ‘Mutator’ are put in the same package
-Create a public interface called ‘ReadOnlyIF’ that has only the read operations of ‘Mutable’.




* *



Architectural / Multi-Layer / Context:
-The multi-layer pattern helps to organize software into hierarchical layers, e.g. UI/Application/Data storage and transmission
Solution:
-Increase cohesion amongst components of each layer.
-Create API for each layer.








Client-Server / Context:
-In client-server pattern, the client requests and expects service, which is the responsibility of the server to provide.
Solution:
-Variants of the Client-Server patterns include the three-tier (client-server-database) or the peer-to-peer model in which client-server roles are interchangeable

«communications»

Broker
/ Context:
-This pattern helps to provide location transparency so that an object can call methods without knowing the location of the other object.
Solution:
-Use Proxy design pattern
-E.g. Using CORBA to provide location transparency



Transaction Proc
/ Context:
-A pattern in which the transaction dispatcher accepts transactions and dispatches them to handlers.
Solution:
-Use dynamic binding of procedure variables and polymorphism to provide transaction processing capability.
-Locking mechanism e.g. 2Phase Lock may be necessary for serialization and multi-transaction handling.





Pipe-and-Filter
/ Context:
-This pattern is used when data/information is passed through a series of tasks, each of which transforms it. Any task may be replaced with another when needed.


Model View Controller
/ Context:
-This pattern is used to decouple User Interface from the rest of the system.
Solution:
-The model part of the pattern represents the functional aspect (database), the Controller control interaction with the model, while the View represents the display.
create/update


notify modify

Singleton Pattern:

When you want to have a class that has just one instance, you can use the Singleton pattern to design its code.

Applications:

  • Print Spooler
  • Accounting System for a company
  • Library Loan Policy
  • Taxation Grades

Pattern Tabulation:

Purpose / Pattern Name / Features
Creational / Singleton / Context:
-Ensures that a class has one instance, e.g. one database per company.
Solution:
-There is a private constructor that ensures no other class is able to recreate another instance



Benefits of Using Singleton Design Pattern:

  • No need to have Global variables
  • Single access point (Functional Cohesion)
  • Control the number of instances
  • Sub-classing is permitted.

How to ensure that a single Instance exists:

  • Make the class responsible to keep a track of its instance(s).
  • Applications to use a common access point to this class
  • Ensure that any requests to create instances are rejected

Member

Functions

Attributes

Strategy:

Hide the operation that creates the instance

Example Code:

Class Singleton

{

public:

static Singleton* InstanceMemberFunction ( );

protected:

Singleton ( );

private:

static Singleton* _instanceMemberVar;

Singleton* Singleton::_instanceMemberVar == 0; // Ptr to the unique inst.

Singleton* Singleton::InstanceMembeFunction ( )

{

if ( _instanceMemberVar == 0 )

// You can control the number of instances here

{

_instanceMemberVar = new Singleton;

// You can also create new SingletonSubClassX, if you have defined it

}

return _instanceMemberVar;

}

}

Proxy Pattern:

Uses of the Proxy Pattern:

  1. Remote Proxy: Provides a local representative of remote objects.
  1. Virtual Proxy: Creates expensive objects whenever needed, e.g. Document Editor downloading text and images from a disk.
  1. Protection Proxy: Provide protection and controls access to precious objects, e.g. database tables.
  1. Implementing Smart Pointer: Replacement for conventional pointer that performs additional actions such as:

-Counting the number of references to a real object. The object may not deleted unless the ref count = 0

-Checking that the object is locked prior to update.

There are situations where you have to combine proxy roles, e.g. Internet Browser uses both 1 and 2.

Virtual Proxy Example : (Ref: Design Pattern, Erich Gamma, Addison Wesley)

Example Code Virtual Proxy:

Class Graphic

{public:

virtual ~Graphic ();

virtual void Draw (const Point& AT) = 0;

virtual const Point& GetBuffer () = 0;

virtual void Load (InStream& From) = 0;

virtual void Store (OutStream& To) = 0;

protected:

Graphic ();

};

Class ImageProxy : Public Graphic

{ public:

ImageProxy (const char * imageFile);

virtual ~ImageProxy ();

virtual void Draw (const Point& AT);

virtual const Point& GetBuffer ();

virtual void Load (InStream& From);

virtual void Store (OutStream& To);

protected:

Image * GetImage();

private:

Image * _image;

Point _Buffer;

Char * _fileName;

};

The Constructor of ImageProxy

  1. Saves a local copy of the File Name that stores the image
  2. Initializes _buffer and _image variables

GetImage method:

Image* ImageProxy::GetImage ()

{ if (_image == 0)

{ _image = new Image (_fileName);//Real Image

}

return _image;

}

The GetBuffer Method returns the cached image (if already built). Otherwise it loads the image from the file using the method

Const Point& ImageProxy::GetBuffer()

{ if (_buffer == Zero)

{ _buffer = GetImage->GetBuffer();

}

return _buffer;

};

------

Class Image : Public Graphic // Class handling the real I/O

{ public:

Image (const char * File);

virtual ~Image();

virtual void Draw (const Point& AT);

virtual const Point& GetBuffer ();

virtual void Load (InStream& From);

virtual void Store (OutStream& To);

private:

//;

};

Some Practicalities:

Implementing Associations:

  1. Binary: One to One
  2. Many to One: m-1
  3. Many to Many: m-n:

Binary:

Class Person

{

public:

Person (char* N, char* Dob, Char g);

Person* GetSIN ( ) { return SIN;}

void setSIN (Person* p); // sets the association

~Person ();

private:

char Name [30];

char DateOfBirth [16];

char gender;

Person* SIN;

}

Many to One:

Class Person

{

public:

Person (char* N, char* Dob, Char g);

Person* GetFather ( ) { return father;}

Person* GetMother ( ) { return mother;}

Person GetChildren ();

void addChild (Person* p)

{ children.insert (p) }; // add to the database

void setFather (Person* p)

{ father = p;

if (p)

{ p->addChild(this);

return;

}

}

void setMother (Person* p) ..

~Person ();

private:

char Name [30];

char DateOfBirth [16];

char gender;

Person* father;

Person* mother;

}

Many to Many: (m-n) Can be implemented by