![]() |
Platforms, Frameworks & Libraries »
Libraries »
General
Advanced
License: The Code Project Open License (CPOL)
Generic Pool: Policy based designBy Rohit JoshiGeneric Pool: Policy based design. |
C, VC7.1WinXP, MFC, VS.NET2003, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
In my previous article (Template based Generic Pool using C++), I explained how to implement generic pool using templates and C++. There are a few limitations in this pool design, and I thought it would be good learning experience to overcome these limitations by applying the policy based design approach.
Readers are assumed to have basic understanding of C++, templates, and STL. I will not go in to details of policy based design. There are plenty of resources available on the Internet. You can also refer to one of the best C++ books, Modern C++ Design by Andrei Alexandrescu, or his article (Policy-Based Class Design in C++). He has explained policy based design in details.
Andrei describes policy based class as:
�policy-based class design fosters assembling a class with complex behavior out of many little classes (called policies), each of which takes care of only one behavioral or structural aspect. As the name suggests, a policy establishes an interface pertaining to a specific issue. You can implement policies in various ways as long as you respect the policy interface�.
Note:
Let�s find out different requirements for the pool in real life scenario.
PoolMgr class. If you change the container, you will have to change the implementation completely.
Let�s look at each requirement and see how we can fulfill it. For the 1st requirement, we need two types of connection objects to be stored in a pool, connections with ID (identifiable) and connections without ID (Generic). This can be implemented using �HasIdPolicy� which has two types of classes.
Note: I could have used structure, but class would be clearer to �C� programmers.
class WithId { public: static bool HasId() { return true;} }; class WithoutId { public: static bool HasId() { return false;} };
For the 2nd requirement, we need two types of classes. One which expires after certain amount of idle time, other that does not expire through out the life time of the process. This can be implemented by �ObjectExpirationPolicy� which has two classes as:
class ObjectWithExpiration { public: // is object expire static bool IsExpire() {return true;�.} }; class ObjectWithoutExpiration { public: // is expire :no static bool IsExpire(){return false;} };
For 3rd requirement, we can define �ObjectCreationPolicy� were connection creation and destroy functionality is implemented. User can define his custom creation policy. I have defined default creation policy as:
template<class Object> class ObjectCreationDefault //: public ObjectCreationPolicyBase<Object> { public: // create and initialize the object static Object* Create() { return new Object(); } // Uninitialize and destroy the object static Destroy(Object* pObj) { if(pObj) { delete pObj; } pObj = NULL; } // Validate the object if not valid static bool Validate(Object *pObj) { return true; } };
For 4th requirement, we need to implement �ContainerInterface�. I have implemented two interfaces as below. I have derived these two classes from ContainerInterfaceBase which defines the containers as member variables of base class which are inherited by both of these policy classes.
These are the template based classes which use above defined policies. These policy classes are dependent on class �HasIdPolicy� and two types of traits which define the actual container. I will explain traits later in this article. Both of these traits define specific type based on selected policies.
template<class Object, class HasIdPolicy, template<CLASS> class CreationPolicy, class ObjectExpirationPolicy, typename HolderTraits = ObjectHolderTraits< Object, CreationPolicy,ObjectExpirationPolicy>, typename ContainerTraits = ObjectContainerTraits<SmartHolder, HasIdPolicy> > class ContainerInterfaceBase { public: ContainerInterfaceBase(){} virtual ~ContainerInterfaceBase(){} protected: ....... Container m_oFree; Container m_oReserved; }; template<class Object, class HasIdPolicy = WithId, template<class> class CreationPolicy = ObjectCreationDefault, class ObjectExpirationPolicy = ObjectWithExpiration, typename HolderTraits = ObjectHolderTraits< Object, CreationPolicy,ObjectExpirationPolicy>, typename ContainerTraits = ObjectContainerTraits< Loki::SmartPtr<typename HolderTraits::ObjectHolder>, WithId> > class ContainerInterfaceWithId {.....};
and
template<class Object, class HasIdPolicy = WithoutId, template<class> class CreationPolicy = ObjectCreationDefault, class ObjectExpirationPolicy = ObjectWithExpiration, typename HolderTraits = ObjectHolderTraits< Object, CreationPolicy,ObjectExpirationPolicy>, typename ContainerTraits = ObjectContainerTraits< Loki::SmartPtr<typename HolderTraits::ObjectHolder>, WithId> > class ContainerInterfaceWithoutId {.....};
For 5th requirement, we will define �PoolSizeType� policy and it has two class implementations as:
class PoolSizeFixed { public: // Is Sizable static bool IsSizable() { return false; } ..... }; class PoolSizeDynamic { public: // is pool sizable static bool IsSizable() { return true; } ..... };
We have found the solution to fulfill these requirements. Now, let's look at how can we use these policies. If we want to use the �ObjectExpirationPolicy�, then timestamp of the connection creation/access time needs to be stored along with connection object. One way to do is to modify the connection class and add timestamp member variable and get/set methods on this class. Another approach is to store the timestamp along with connection object into another wrapper class as connection holder.
The first approach is easier but the disadvantage is that all the connection classes need to be modified to have connection expiration supported which is not feasible. More over, you might want to store some connections/objects which you do not have access to source code� So, I decided to implement the 2nd approach where we will create connection holder class based on �ObjectExpirationpolicy�. Holder class will be more useful when you want to store additional information like username and password of the server etc�
In the case of connections not expiring, we don�t need to store the timestamp value. We will create two types of connection holder classes, one with connection timestamp as member variables, other without timestamp.
Other problem is how do we know which holder class and when to use based on selected policy? Should we create instance of both the classes and use �if else� to check which one to use based on selected policy?
To overcome this problem, we need to look at the traits. What are traits?
Nathan C. Myers gives short definition of traits in his article (Traits: a new and useful template technique) as:
�A class used in place of template parameters. As a class, it aggregates useful types and constants; as a template, it provides an avenue for that "extra level of indirection" that solves all software problems.�
template<class Object, template<typename> class ObjectCreationPolicy, class ObjectExpirationPolicy> class ObjectHolderTraits;
�ObjectHolderTraits� uses creation policy to create and destroy the connections, and expiration policy to define timestamp variable.
We will partially specialize this template with �ObjectWithExpiration� and �ObjectWithoutExpiration� policies as:
template<class Object, template<typename> class ObjectCreationPolicy> class ObjectHolderTraits<Object,ObjectCreationPolicy,ObjectWithExpiration> { class Holder { . . . . ; }; template<class Object, template<typename> class ObjectCreationPolicy> class ObjectHolderTraits<Object,ObjectCreationPolicy,ObjectWithoutExpiration> { class Holder { . . . . ; };
4th requirement is partially fulfilled by �ContainerInterfacePolicy� by adding the abstraction layer to access the containers. But what if we want to use vector instead of list or multimap instead of map? Again, traits provide the solution to this problem.
We will define object container traits and partially specialize with �HasIdPolicy�.
template<class Object, class HasIdPolicy> class ObjectContainerTraits;
Here, we have used STL �List� container for without ID policy, and STL �map� container for without ID policy.
template<class Object> class ObjectContainerTraits<Object,WithoutId> { public: typedef list<Object> Container; }; template<class Object> class ObjectContainerTraits<Object,WithId> { public: typedef map<string,Object> Container; };
The objects inside the container are stored as smart pointers. There are two reasons.
To solve these problems, smart pointer is the way to go where we don�t have to worry about memory leak and reference counting. I have used Loki::SmartPtr as smart pointers. For more details: SourceForge.
It�s time to put all together and implement the Generic Pool. The Pool class is supposed to be implemented as singleton. I decide to use policy based designed Loki::SingletonHolder<> class which is part of the Loki library, instead of writing my own singleton class.
There is a really good article (Singleton Pattern: A review and analysis of existing C++ implementations) on The Code Project.
The class is defined as:
template<class Object, class HasIdPolicy = WithoutId, class PoolSizePolicy = PoolSizeFixed, class ObjectExpirationPolicy = ObjectWithExpiration, template<class> class CreationPolicy = ObjectCreationDefault, typename HolderTraits = ObjectHolderTraits<Object,CreationPolicy, ObjectExpirationPolicy>, typename ContainerTraits = ObjectContainerTraits<Loki::SmartPtr<typename HolderTraits::ObjectHolder>, HasIdPolicy>, typename ContainterInterfaceTraits = ContainerInterfaceTraits<Object, HasIdPolicy> > class PoolMgr { public: void Init(unsigned nPoolSize, unsigned nExpirationTimeSec); Object* Checkout(string &sId) {...} //withid Object* Checkout() {...} //without id bool Checkin(string &sId) {...} //withid bool Checkin(Object *pObj) {...} // without id void ResizePool(unsigned nNewSize){...} //resize/reset private: typename ContainterInterfaceTraits::ContainerInterfacePolicy m_oContainer; };
In this class, I have provided basic functionality of the pool. You can modify it to have custom behavior. E.g., how long caller function should wait before returning when pool is full and it�s not sizable (See 1). If object expiration policy is allowed, you can spawn a thread which will wake up at certain time to clean up the expired connections.
Finally, below is the code to test our pool implementation. Please make sure you use the Loki::SingletonHolder<> class to create an instance of the pool. Try it out by changing different policies to PoolMgr template parameters.
// My connection class. class MyConnection { public: MyConnection() { LOG(LOG_INFO, "MyConnection()"); } ~MyConnection() { LOG(LOG_INFO, "~MyConnection()"); } string & Get() { return m_sString; } void Set(string &sStr) { m_sString = sStr; } private: string m_sString; }; // int main() { // create a singleton using Loki::SingletonHolder //without id { typedef PoolMgr< MyConnection, WithoutId, PoolSizeFixed, ObjectWithExpiration> Pool; Pool *pMgr = &Loki::SingletonHolder:: Instance(); pMgr->Init(1,5); MyConnection *pConn = pMgr->Checkout(); pMgr->Checkin(pConn); pMgr->ResizePool(0); } //with id { typedef PoolMgr< MyConnection, WithId, PoolSizeFixed, ObjectWithExpiration> Pool; Pool *pMgr = &Loki::SingletonHolder:: Instance(); string sId = "1"; pMgr->Init(1,5); MyConnection *pConn = pMgr->Checkout(sId); pMgr->Checkin(sId); pMgr->ResizePool(0); } }
Note: I have included Windows version of Loki library along with my project. You can download latest version at SourceForge.
Please do vote and comment on this article which will help me to improve quality of my next article. Thanks.
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 3 Sep 2004 Editor: Smitha Vijayan |
Copyright 2004 by Rohit Joshi Everything else Copyright © CodeProject, 1999-2009 Web22 | Advertise on the Code Project |