Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2084457
  • 博文数量: 414
  • 博客积分: 10312
  • 博客等级: 上将
  • 技术积分: 4921
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-31 01:49
文章分类

全部博文(414)

文章存档

2011年(1)

2010年(29)

2009年(82)

2008年(301)

2007年(1)

分类: C/C++

2009-01-17 08:37:30

 

Contents

Programming Patterns


Software design patterns are abstractions that help structure system designs. While not new, the concept got defined and gathered some traction in Software Engineering/CS due to the publication of book in October 1994 by the Gang of Four (GoF), that identifies and describes 23 classic software design patterns.

A design pattern is not a static solution, it is not an algorithm. A pattern is a way to describe and address by name (mostly a simplistic description of its goal), a repeatable solution or approach to a common design problem, that is, a common way to solve a generic problem (how generic or complex, depends on how restricted the target goal is). Patterns can emerge on their own or by design, this is why design patterns are useful as an abstraction over the implementation and a help at design stage.

Patterns are commonly found in objected oriented programming languages like C++. They can be seen as a template for how to solve a problem that occurs in many different situations and/or applications. It is not code reuse as it usually does not specify code, but code can be easily created from a design pattern. Object-oriented design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved.

Each design pattern consists of the following parts:

Problem/requirement 
To use a design pattern, we need to go through a mini analysis design that may be coded to test out the solution. This section states the requirements of the problem we want to solve. This is usually a common problem that will occur in more than one application.
Forces 
This section states the technological boundaries, that helps and guides the creation of the solution.
Solution 
This section describes how to write the code to solve the above problem. This is the design part of the design pattern. It may contain class diagrams, sequence diagrams, and or whatever is needed to describe how to code the solution.

Design patterns can be considered as a standardization of commonly agreed best practices to solve specific design problems, one should at least understand them as a way to implement good design patterns within applications. Doing so will reduce the use of inefficient and obscure solutions. Using design patterns speeds up your design and helps to communicate your design to other programmers.

Creational Patterns

In , creational design patterns are that deal with mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or added complexity to the design. Creational design patterns solve this problem by somehow controlling this object creation.

In this section of the book we assume that the reader has enough familiarity with functions, global variables, stack vs. heap, classes, pointers, and static member functions as introduced before.

As we will see there are several creational design patterns, and all will deal with a specific implementation task, that will create a higher level of abstraction to the code base, we will now cover each one.

Builder

The Builder Creational Pattern is used to separate the construction of a complex object from its representation so that the same construction process can create different objects representations.

Problem 
We want to construct a complex object, however we do not want to have a complex constructor member or one that would need many arguments.
Solution 
Define an intermediate object whose member functions define the desired object part by part before the object is available to the client. Build Pattern lets us defer the construction of the object until all the options for creation have been specified.

Factory Method

The Factory Design Pattern is useful in a situation where a design requires the creation of many different types of objects, all of which come from a common base type. The Factory Method will take a description of a desired object at run time, and return a new instance of that object. The upshot of the pattern is that you can take a real world description of an object, like a string read from user input, pass it into the factory, and the factory will return a base class pointer. The pattern works best when a well designed interface is used for the base class, so that the returned object may be used fully without having to cast it, using RTTI.

Problem 
We want to decide at run time what object is to be created based on some configuration or application parameter. When we write the code we do not know what class should be instantiated.
Solution 
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

The following example uses the notion of laptop vs. desktop computer objects with a computer factory.

Let's start by defining out base class (interface) and the derived classes: Desktop and Laptop. Neither of these classes actually "do" anything, but are meant for the illustrative purpose.

 class Computer
 {
 public:
 virtual void Run() = 0;
 virtual void Stop() = 0;
 };
 class Laptop: public Computer
 {
 public:
 virtual void Run(){mHibernating = false;}
 virtual void Stop(){mHibernating = true;}
 private:
 bool mHibernating; // Whether or not the machine is hibernating
 };
 class Desktop: public Computer
 {
 public:
 virtual void Run(){mOn = true;}
 virtual void Stop(){mOn = false;}
 private:
 bool mOn; // Whether or not the machine has been turned on
 };

Now for the actual Factory, returns a Computer, given a real world description of the object

 class ComputerFactory
 {
 public:
 static Computer *NewComputer(const std::string &description)
 {
 if(description == "laptop")
 return new Laptop;
 if(description == "desktop")
 return new Desktop;
 return NULL;
 }
 };

Thanks to the magic of virtual functions, the caller of the factory method can use the returned results, without any knowledge of the true type of the object.

Let's analyze the benefits of this. First, there is a compilation benefit. If we move the interface "Computer" into a separate header file with the factory, we can then move the implementation of the NewComputer() function into a separate implementation file. By doing this, that implementation file for NewComputer() is the only one which needs knowledge of the derived classes. Thus, if a change is made to any derived class of Computer, or a new Computer type is added, the implementation file for NewComputer() is the only file which needs to be recompiled. Everyone who uses the factory will only care about the interface, which will hopefully remain consistent throughout the life of the application.

Also, if a new class needs to be added, and the user is requesting objects through a user interface of some sort, no code calling the factory may need to change to support the additional computer type. The code using the factory would simply pass on the new string to the factory, and allow the factory to handle the new types entirely.

Imagine programming a video game, where you would like to add new types of enemies in the future, each of which has different AI functions, and can update differently. By using a factory method, the controller of the program can call to the factory to create the enemies, without any dependency or knowledge of the actual types of enemies. Now, future developers can create new enemies, with new AI controls, and new drawing member functions, add it to the factory, and create a level which calls the factory, asking for the enemies by name. Combine this method with an description of levels, and developers could create new levels without any need to ever recompile their program. All this, thanks to the separation of creation of objects from the usage of objects.

Abstract Factory

Prototype Design Pattern [A fully initialized instance to be copied or cloned]

A prototype pattern is a creational design pattern used in software development when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects. This pattern is used for example when the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword) is prohibitively expensive for a given application.

To implement the pattern, declare an abstract base class that specifies a pure virtual clone() member function. Any class that needs a "polymorphic constructor" capability derives itself from the abstract base class, and implements the clone() operation.

The client, instead of writing code that invokes the "new" operator on a hard-wired class name, calls the clone() member function on the prototype, calls a factory member function with a parameter designating the particular concrete derived class desired, or invokes the clone() member function through some mechanism provided by another design pattern.

 class CPrototypeMonster
 {
 protected: 
 CString _name;
 public:
 CPrototypeMonster();
 CPrototypeMonster( const CPrototypeMonster& copy );
 ~CPrototypeMonster();
 
 virtual CPrototypeMonster* Clone() const=0; // This forces every derived class to provide an overload for this function.
 void Name( CString name );
 CString Name() const;
 };
 
 class CGreenMonster : public CPrototypeMonster
 {
 protected: 
 int _numberOfArms;
 double _slimeAvailable;
 public:
 CGreenMonster();
 CGreenMonster( const CGreenMonster& copy );
 ~CGreenMonster();
 
 virtual CPrototypeMonster* Clone() const;
 void NumberOfArms( int numberOfArms );
 void SlimeAvailable( double slimeAvailable );
 
 int NumberOfArms() const;
 double SlimeAvailable() const;
 };
 
 class CPurpleMonster : public CPrototypeMonster
 {
 protected:
 int _intensityOfBadBreath;
 double _lengthOfWhiplikeAntenna;
 public:
 CPurpleMonster();
 CPurpleMonster( const CPurpleMonster& copy );
 ~CPurpleMonster();
 
 virtual CPrototypeMonster* Clone() const;
 
 void IntensityOfBadBreath( int intensityOfBadBreath );
 void LengthOfWhiplikeAntenna( double lengthOfWhiplikeAntenna );
 
 int IntensityOfBadBreath() const;
 double LengthOfWhiplikeAntenna() const;
 };
 
 class CBellyMonster : public CPrototypeMonster
 {
 protected:
 double _roomAvailableInBelly;
 public:
 CBellyMonster();
 CBellyMonster( const CBellyMonster& copy );
 ~CBellyMonster();
 
 virtual CPrototypeMonster* Clone() const;
 
 void RoomAvailableInBelly( double roomAvailableInBelly );
 double RoomAvailableInBelly() const;
 };
 
 CPrototypeMonster* CGreenMonster::Clone() const
 {
 return new CGreenMonster(*this);
 }
 
 CPrototypeMonster* CPurpleMonster::Clone() const
 {
 return new CPurpleMonster(*this);
 }
 
 CPrototypeMonster* CBellyMonster::Clone() const
 {
 return new CBellyMonster(*this);
 }

A client of one of the concrete monster classes only needs a reference (pointer) to a CPrototypeMonster class object to be able to call the ‘Clone’ function and create copies of that object. The function below demonstrates this concept:

 void DoSomeStuffWithAMonster( const CPrototypeMonster* originalMonster )
 {
 CPrototypeMonster* newMonster = originalMonster->Clone();
 ASSERT( newMonster );
 
 newMonster->Name("MyOwnMoster");
 // Add code doing all sorts of cool stuff with the monster.
 delete newMonster;
 }

Now originalMonster can be passed as a pointer to CGreenMonster, CPurpleMonster or CBellyMonster.

Prototype

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

  • A prototype pattern is used in software development when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects. This pattern is used for example when the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword) is prohibitively expensive for a given application.
  • Implementation: Declare an abstract base class that specifies a pure virtual clone() method. Any class that needs a "polymorphic constructor" capability derives itself from the abstract base class, and implements the clone() operation.
  • Here the client code first invokes the factory method. This factory method, depending on the parameter, finds out concrete class. On this concrete class call to the Clone() method is called and the object is returned by the factory method.
  • This is sample code which is a sample implementation of Prototype method. We have the detailed description of all the components here.
    • "Record" class which is a pure virtual class which is having pure virtual method "Clone()".
    • "CarRecord", "BikeRecord" and "PersonRecord" as concrete implementation of "Record" class.
    • An enum RECORD_TYPE_en as one to one mapping of each concrete implementation of "Record" class.
    • "RecordFactory" class which is having Factory method "CreateRecord(...)". This method requires an enum RECORD_TYPE_en as parameter and depending on this parameter it returns the concrete implementation of "Record" class.


 /**
 * Implementation of Prototype Method 
 **/
 #include 
 #include 
 
 using namespace std;
 
 enum RECORD_TYPE_en
 {
 CAR,
 BIKE,
 PERSON
 };
 
 /**
 * Record is the Prototype
 */
 
 class Record
 {
 public :
 
 Record() {}
 
 ~Record() {}
 
 Record* Clone()=0;
 
 virtual void Print()=0;
 };
 
 /**
 * CarRecord is Concrete Prototype
 */
 
 class CarRecord : public Record
 {
 private :
 string m_oStrCarName;
 
 u_int32_t m_ui32ID;
 
 public :
 
 CarRecord(string _oStrCarName,u_int32_t _ui32ID)
 : Record(), m_oStrCarName(_oStrCarName),
 m_ui32ID(_ui32ID)
 {
 }
 
 CarRecord(CarRecord& _oCarRecord)
 : Record()
 {
 m_oStrCarName = _oCarRecord.m_oStrCarName;
 m_ui32ID = _oCarRecord.m_ui32ID;
 }
 
 ~CarRecord() {}
 
 Record* Clone()
 {
 return new CarRecord(*this);
 }
 
 void Print()
 {
 cout << "Car Record" << endl
 << "Name : " << m_oStrCarName << endl
 << "Number: " << m_ui32ID << endl << endl;
 }
 };
 
 
 /**
 * BikeRecord is the Concrete Prototype
 */
 
 class BikeRecord : public Record
 {
 private :
 string m_oStrBikeName;
 
 u_int32_t m_ui32ID;
 
 public :
 BikeRecord(string _oStrBikeName,u_int32_t _ui32ID)
 : Record(), m_oStrBikeName(_oStrBikeName),
 m_ui32ID(_ui32ID)
 {
 }
 
 BikeRecord(BikeRecord& _oBikeRecord)
 : Record()
 {
 m_oStrBikeName = _oBikeRecord.m_oStrBikeName;
 m_ui32ID = _oBikeRecord.m_ui32ID;
 }
 
 ~BikeRecord() {}
 
 Record* Clone()
 {
 return new BikeRecord(*this);
 }
 
 void Print()
 {
 cout << "Bike Record" << endl
 << "Name : " << m_oStrBikeName << endl
 << "Number: " << m_ui32ID << endl << endl;
 }
 };
 
 
 /**
 * PersonRecord is the Concrete Prototype
 */
 
 class PersonRecord : public Record
 {
 private :
 string m_oStrPersonName;
 
 u_int32_t m_ui32Age;
 
 public :
 PersonRecord(string _oStrPersonName, u_int32_t _ui32Age)
 : Record(), m_oStrPersonName(_oStrPersonName),
 m_ui32Age(_ui32Age)
 {
 }
 
 PersonRecord(PersonRecord& _oPersonRecord)
 : Record()
 {
 m_oStrPersonName = _oPersonRecord.m_oStrPersonName;
 m_ui32Age = _oPersonRecord.m_ui32Age;
 }
 
 ~PersonRecord() {}
 
 Record* Clone()
 {
 return new PersonRecord(*this);
 }
 
 void Print()
 {
 cout << "Person Record" << endl
 << "Name : " << m_oStrPersonName << endl
 << "Age : " << m_ui32Age << endl << endl ;
 }
 };
 
 
 /**
 * RecordFactory is the client
 */
 
 class RecordFactory
 {
 private :
 map m_oMapRecordReference;
 
 public :
 RecordFactory()
 {
 m_oMapRecordReference[CAR] = new CarRecord("Ferrari", 5050);
 m_oMapRecordReference[BIKE] = new BikeRecord("Yamaha", 2525);
 m_oMapRecordReference[PERSON] = new PersonRecord("Tom", 25);
 }
 
 ~RecordFactory()
 {
 delete m_oMapRecordReference[CAR];
 delete m_oMapRecordReference[BIKE];
 delete m_oMapRecordReference[PERSON];
 }
 
 Record* CreateRecord(RECORD_TYPE_en enType)
 {
 return m_oMapRecordReference[enType]->Clone();
 }
 };
 
 int main()
 {
 RecordFactory* poRecordFactory = new RecordFactory();
 
 Record* poRecord;
 poRecord = poRecordFactory->CreateRecord(CAR);
 poRecord->Print();
 delete poRecord;
 
 poRecord = poRecordFactory->CreateRecord(BIKE);
 poRecord->Print();
 delete poRecord;
 
 poRecord = poRecordFactory->CreateRecord(PERSON);
 poRecord->Print();
 delete poRecord;
 
 delete poRecordFactory;
 return 0;
 }

Singleton

The term Singleton refers to an object that can only be instantiated once. This pattern is generally used where a global variable would have otherwise been used. The main advantage of the singleton is that its existence is guaranteed. Other advantages of the design pattern include the clarity, from the unique access, that the object used is not on the local stack. Some of the downfalls of the object include that, like a global variable, it can be hard to tell what chunk of code corrupted memory, when a bug is found, since everyone has access to it.

TODO

TODO
Other pros/cons of the use of singletons.

Let's take a look at how a Singleton differs from other variable types.

Like a global variable, the Singleton exists outside of the scope of any functions. Traditional implementation uses a static member function of the Singleton class, which will create a single instance of the Singleton class on the first call, and forever return that instance. The following code example illustrates the elements of a C++ singleton class, that simply stores a single string.

 class StringSingleton
 {
 public:
 // Some accessor functions for the class, itself
 std::string GetString() const 
 {return mString;}
 void SetString(const std::string &newStr)
 {mString = newStr;}
 
 // The magic function, which allows access to the class from anywhere
 // To get the value of the instance of the class, call:
 // StringSingleton::Instance().GetString();
 static StringSingleton &Instance()
 {
 // This line only runs once, thus creating the only instance in existence
 static StringSingleton *instance = new StringSingleton;
 // dereferencing the variable here, saves the caller from having to use 
 // the arrow operator, and removes tempation to try and delete the 
 // returned instance.
 return *instance; // always returns the same instance
 }
 
 private: 
 // We need to make some given functions private to finish the definition of the singleton
 StringSingleton(){} // default constructor available only to members or friends of this class
 
 // Note that the next two functions are not given bodies, thus any attempt 
 // to call them implicitly will return as compiler errors. This prevents 
 // accidental copying of the only instance of the class.
 StringSingleton(const StringSingleton &old); // disallow copy constructor
 const StringSingleton &operator=(const StringSingleton &old); //disallow assignment operator
 
 // Note that although this should be allowed, 
 // some compilers may not implement private destructors
 // This prevents others from deleting our one single instance, which was otherwise created on the heap
 ~StringSingleton(){} 
 private: // private data for an instance of this class
 std::string mString;
 };

Variations of Singletons:

TODO

TODO
Discussion of Meyers Singleton and any other variations.

Applications of Singleton Class:

One common use of the singleton design pattern is for application configurations. Configurations may need to be accessible globally, and future expansions to the application configurations may be needed. The subset C's closest alternative would be to create a single global struct. This had the lack of clarity as to where this object was instantiated, as well as not guaranteeing the existence of the object.

Take, for example, the situation of another developer using your singleton inside the constructor of their object. Then, yet another developer decides to create an instance of the second class in the global scope. If you had simply used a global variable, the order of linking would then matter. Since your global will be accessed, possibly before main begins executing, there is no definition as to whether the global is initialized, or the constructor of the second class is called first. This behavior can then change with slight modifications to other areas of code, which would change order of global code execution. Such an error can be very hard to debug. But, with use of the singleton, the first time the object is accessed, the object will also be created. You now have an object which will always exist, in relation to being used, and will never exist if never used.

A second common use of this class is in updating old code to work in a new architecture. Since developers may have used to use globals liberally, pulling these into a single class and making it a singleton, can allow an intermediary step to bringing a structural program into an object oriented structure.

Structural Patterns

Adapter

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

Bridge

The Bridge Pattern is used to separate out the interface from its implementation. Doing this gives the flexibility so that both can vary independently.

Composite

Composite lets clients treat individual objects and compositions of objects uniformly. The Composite pattern can represent both the conditions. In this pattern, one can develop tree structures for representing part-whole hierarchies.

Decorator

The decorator pattern helps to attach additional behavior or responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. This is also called “Wrapper”.

Facade

The Facade Pattern hides the complexities of the system by providing an interface to the client from where the client can access the system on an unified interface. Facade defines a higher-level interface that makes the subsystem easier to use.

Flyweight

It is the use of sharing mechanism by which you can avoid creating a large number of object instances to represent the entire system by using a smaller set fine-grained objects efficiently. A flyweight is a shared object that can be used in multiple contexts simultaneously. The flyweight will act as an independent object in each context, becoming indistinguishable from an instance of the object that’s not shared. To decide if some part of a program is a candidate for using Flyweights, consider whether it is possible to remove some data from the class and make it extrinsic.

Proxy

The Proxy Pattern will provide an object a surrogate or placeholder for another object to control access to it. It is used when you need to represent a complex object with a simpler one. If creation of an object is expensive, it can be postponed until the very need arises and meanwhile a simpler object can serve as a placeholder. This placeholder object is called the “Proxy” for the complex object.

Curiously Recurring Template

This technique is known more widely as a mixin. Mixins are described in the literature to be a powerful tool for expressing abstractions.

Behavioral Patterns

[Chain of Responsibility

Chain of Responsibility pattern has the intent to avoid coupling the sender of a request to its receiver by giving more then one object a chance to handle the request. Chains the receiving objects and passes the requests along the chain until an object handles it.

Command

Command pattern is an Object behavioral pattern that decouples sender and receiver by encapsulating a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. It can also be thought as an object oriented equivalent of call back method.

Call Back: It is a function that is registered to be called at later point of time based on user actions.

Interpreter

Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

Iterator

The 'iterator' design pattern is used liberally within the STL for traversal of various containers. The full understanding of this will liberate a developer to create highly reusable and easily understandable data containers.

The basic idea of the iterator is that it permits the traversal of a container (like a pointer moving across an array). However, to get to the next element of a container, you need not know anything about how the container is constructed. This is the iterators job. By simply using the member functions provided by the iterator, you can move, in the intended order of the container, from the first element to the last element.

Let's start by considering a traditional single dimensional array with a pointer moving from the start to the end. This example assumes knowledge of pointer arithmetic. Note that the use of "it" or "itr," henceforth, is a short version of "iterator."

 const int ARRAY_LEN = 42;
 int *myArray = new int[ARRAY_LEN];
 // Set the iterator to point to the first memory location of the array
 int *arrayItr = myArray;
 // Move through each element of the array, setting it equal to its position in the array
 for(int i = 0; i < ARRAY_LEN; ++i)
 {
 // set the value of the current location in the array
 *arrayItr = i;
 // by incrementing the pointer, we move it to the next position in the array.
 // This is easy for a contiguous memory container, since pointer arithmetic 
 // handles the traversal.
 ++arrayItr;
 }
 // Don't be messy, clean up after yourself
 delete[] myArray;

This code works very quickly for arrays, but how would we traverse a linked list, when the memory is not contiguous? Consider the implementation of a rudimentary linked list as follows:

TODO

TODO
test compiling this, check for syntax errors.

 class IteratorCannotMoveToNext{}; // Error class
 class MyIntLList
 {
 public:
 // The Node class represents a single element in the linked list. 
 // The node has a next node and a previous node, so that the user 
 // may move from one position to the next, or step back a single 
 // position. Notice that the traversal of a linked list is O(N), 
 // as is searching, since the list is not ordered.
 class Node
 {
 public:
 Node():mNextNode(0),mPrevNode(0),mValue(0){}
 Node *mNextNode;
 Node *mPrevNode;
 int mValue;
 };
 MyIntLList():mSize(0) 
 {}
 ~MyIntLList()
 {
 while(!Empty())
 pop_front();
 } // See expansion for further implementation;
 int Size() const {return mSize;}
 // Add this value to the end of the list
 void push_back(int value)
 {
 Node *newNode = new Node;
 newNode->mValue = value;
 newNode->mPrevNode = mTail;
 mTail->mNextNode = newNode;
 mTail = newNode;
 ++mSize;
 }
 // Remove the value from the beginning of the list
 void pop_front()
 {
 if(Empty())
 return;
 Node *tmpnode = mHead;
 mHead = mHead->mNextNode
 delete tmpnode;
 --mSize;
 }
 bool Empty()
 {return mSize == 0;}
 
 // This is where the iterator definition will go, 
 // but lets finish the definition of the list, first
 
 private:
 Node *mHead;
 Node *mTail;
 int mSize;
 };

This linked list has non-contiguous memory, and is therefore not a candidate for pointer arithmetic. And we dont want to expose the internals of the list to other developers, forcing them to learn them, and keeping us from changing it.

This is where the iterator comes in. The common interface makes learning the usage of the container easier, and hides the traversal logic from other developers.

Let's examine the code for the iterator, itself.

 /*
 * The iterator class knows the internals of the linked list, so that it 
 * may move from one element to the next. In this implementation, I have 
 * chosen the classic traversal method of overloading the increment 
 * operators. More thorough implementations of a bi-directional linked 
 * list would include decrement operators so that the iterator may move 
 * in the opposite direction.
 */
 class Iterator
 {
 public:
 Iterator(Node *position):mCurrNode(position){}
 // Prefix increment
 const Iterator &operator++()
 {
 if(mCurrNode == 0 || mCurrNode->mNextNode == 0)
 throw IteratorCannotMoveToNext();e
 mCurrNode = mCurrNode->mNextNode;
 return *this;
 }
 // Postfix increment
 Iterator operator++(int)
 {
 Iterator tempItr = *this;
 ++(*this);
 return tempItr;
 }
 // Dereferencing operator returns the current node, which should then 
 // be dereferenced for the int. TODO: Check syntax for overloading 
 // dereferencing operator
 Node * operator*()
 {return mCurrNode;}
 // TODO: implement arrow operator and clean up example usage following
 private:
 Node *mCurrNode;
 };
 // The following two functions make it possible to create 
 // iterators for an instance of this class.
 // First position for iterators should be the first element in the container.
 Iterator Begin(){return Iterator(mHead);}
 // Final position for iterators should be one past the last element in the container.
 Iterator End(){return Iterator(0);}

With this implementation, it is now possible, without knowledge of the size of the container or how its data is organized, to move through each element in order, manipulating or simply accessing the data. This is done through the accessors in the MyIntLList class, Begin() and End().

 // Create a list
 MyIntLList mylist;
 // Add some items to the list
 for(int i = 0; i < 10; ++i)
 myList.push_back(i);
 // Move through the list, adding 42 to each item.
 for(MyIntLList::Iterator it = myList.Begin(); it != myList.End(); ++it)
 (*it)->mValue += 42;
TODO

TODO

  • Discussion of iterators in the STL, and the usefulness of iterators within the algorithms library.
  • Iterators best practices
  • Warnings on creation of and usage of

Mediator

Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.

Memento

Without violating encapsulation the memento Pattern will capture and externalize an object’s internal state so that the object can be restored to this state later. Though the Gang of Four uses friend as a way to implement this pattern it is not the best design. It can also be implemented using PIMPL. Best Use case is 'Undo-Redo' in an editor.

Observer

The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Problem 
In one place or many places in the application we need to be aware about a system event or an application state change. We'd like to have a standard way of subscribing to listening for system events and a standard way of notifying the interested parties. The notification should be automated after an interested party subscribed to the system event or application state change. There also should be a way to unsubscribed.
Forces 
Observers and observables probably should be represented by objects. The observer objects will be notified by the observable objects.
Solution 
After subscribing the listening objects will be notified by a way of method call.

State

The State Pattern allows an object to alter its behavior when its internal state changes. The object will appear as having changed its class.

Strategy

Defines a family of algorithms, encapsulates each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients who use it.

Template Method

By defining a skeleton of an algorithm in an operation, deferring some steps to subclasses, the Template Method lets subclasses redefine certain steps of that algorithm without changing the algorithms structure.

Visitor

The Visitor Pattern will represent an operation to be performed on the elements of an object structure by letting you define a new operation without changing the classes of the elements on which it operates.

Model-View-Controller (MVC)

pattern often used by applications that need the ability to maintain multiple views of the same data.


 

阅读(1217) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~