Design Patterns - 2562
Design Patterns - 2562
Most of the modern languages and tools use object oriented design to accomplish this task of
solving business problems. Designing a software system is challenging because it not only
needs to meet the identified requirements but also needs to be ready for future extensions and
modifications. A software design problem may have more than one solution. However, the
solution you pick should be the best in a given context. That is where design patterns come
into the picture.
Name – Name expresses the purpose of design pattern and can be uniquely identified among
developers
Intent – It discusses about the pattern. It tells what is the intention of the design pattern
Problem -- The problem describes when to apply the pattern. It explains the problem and its
context. It might describe specific design problems such as how to represent algorithms as
objects. It might describe class or object structures that are symptomatic of an inflexible
design.
Solution – The solution describes the elements that make up the design, their relationships,
responsibilities, and collaborations. The solution doesn’t describe a particular concrete design
or implementation, because a pattern is like a template that can be applied in many different
situations.
Consequences -- his section explains both the positive and negative implications of using the
design pattern. Positive implications might be increased flexibility, lower memory usage,
easier extensibility, support for particular functionality, or simplified usage. Negative
implications might be inefficient behaviour in particular cases, complex class structure for
certain problems, loss of guarantees of system behaviour, or overly general design with
attendant loss of performance or storage costs. It is important that authors of design patterns
present, and readers of design patterns understand, positive as well as negative consequences.
There are three types of design patterns
This pattern can be divided into class-creation patterns and object-creational patterns. While
class-creation patterns use inheritance effectively in the instantiation process, object-creation
patterns use delegation effectively to get the job done.
This patterns are all about Class and Object composition. Structural class-creation patterns
use inheritance to compose interfaces. Structural object-patterns define ways to compose
objects to obtain new functionality.
This patterns are all about Class's objects communication. Behavioural patterns are those
patterns that are most specifically concerned with communication between objects.
Design Patterns
There are two recurring themes in these patterns. First, they all encapsulate knowledge about
which concrete classes the system uses. Second, they hide how instances of these classes are
created and put together. All the system at large
knows about the objects is their interfaces as defined by abstract classes. Consequently, the
creational patterns give you a lot of flexibility in what gets created, who creates it, how it gets
created, and when. They let you configure
a system with "product" objects that vary widely in structure and functionality. Configuration
can be static (that is, specified at compile-time) or dynamic (at run-time).
Sometimes creational patterns are competitors. For example, there are cases when either
Prototype or Abstract Factory could be used profitably. Some times they are complementary:
Builder can use one of the other patterns to implement which components get built. Prototype
can use Singleton in its implementation.
Prototype Pattern
This is a Creational pattern that should be used when the type of objects to create is
determined by a 'prototypical instance', which is cloned to produce new objects.
Avoids sub-classes of an object creator in the client application (like abstract factory does)
Biggest reason for using it is – it avoids inherent cost of creating a new object in the standard
way (via new) in cases where producing initial values for the fields of the object is costly.
Client Prototype
Operation ( ) Clone ( )
Example
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
// Prototype
class Document
{
public:
virtual Document* clone() const = 0;
virtual void store() const = 0;
virtual ~Document() { }
};
// Concrete prototypes : xmlDoc, plainDoc, spreadsheetDoc
private:
static Document* mDocTypes[N];
};
Document* DocumentManager::mDocTypes[] =
{
0, new xmlDoc, new plainDoc, new spreadsheetDoc
};
// for_each op ()
struct Destruct
{
void operator()(Document *a) const {
delete a;
}
};
// Client
int main() {
vector<Document*> docs(N);
int choice;
cout << "quit(0), xml(1), plain(2), spreadsheet(3): \n";
while (true) {
cout << "Type in your choice (0-3)\n";
cin >> choice;
if (choice <= 0 || choice >= N)
break;
docs[choice] = DocumentManager::makeDocument( choice );
}
Destruct d;
// this calls Destruct::operator()
for_each(docs.begin(), docs.end(), d);
return 0; }
OUTPUT
Client Prototype
Participants
Consequences
Benefits of the Prototype pattern are listed below.
4. Reduced subclassing.
The Prototype pattern lets you clone a prototype instead of asking a factory method to
make a new object. Hence you don't need a Creator class hierarchy at all. This benefit
applies primarily to languages like C++ that don't treat classes as first-class objects.
Languages that do, like Smalltalk and Objective C, derive less benefit, since you can
always use a class object as a creator. Class objects already act like prototypes in
these languages.
The main liability of the Prototype pattern is that each subclass of Prototype
must implement the Clone operation, which may be difficult. For example, adding
Clone is difficult when the classes under consideration already exist.
Implementing Clone can be difficult when their internals include objects that
don't support copying or have circular references.
Implementation
2. Implementing the Clone operation. The hardest part of the Prototype pattern is
implementing the Clone operation correctly. It's particularly tricky when object
structures contain circular references. Most languages provide some support for
cloning objects.
3. Initializing clones. While some clients are perfectly happy with the clone as is, others
will want to initialize some or all of its internal state to values of their choosing. You
generally can't pass these values in the Clone operation, because their number will
vary between classes of prototypes. Some prototypes might need multiple
initialization parameters; others won't need any. Passing parameters in the Clone
operation precludes a uniform cloning interface.
It might be the case that your prototype classes already define operations for
(re)setting key pieces of state. If so, clients may use these operations immediately
after cloning. If not, then you may have to introduce an Initialize operation (see the
Sample Code section) that takes initialization parameters as arguments and sets the
clone's internal state accordingly. Beware of deep-copying Clone operations—the
copies may have to be deleted (either explicitly or within Initialize) before you
reinitialize them.