2010-06-11 7 views
2

Je veux diviser une classe afin qu'elle soit découplée de la logique d'exécution de certaines tâches afin que les utilisateurs puissent écrire de nouvelles stratégies comme ils le souhaitent sans interférer avec le modèle central. Donc, je veux utiliser la classe de stratégie mais basé sur un modèle sans avoir à l'utilisateur de la stratégie à templatised:modèle modèle de stratégie

class Model { 
    ... 
    boost::shared_ptr< Strategy <T> > m_pStrategy; 
    ... 
    public: 
    template < typename T > 
    void DoSomething() { m_pStrategy <T> ::DoSomething(); } 
    }; 

Je voudrais la fonction DoSomething de ne pas être templated. Y a-t-il un autre moyen de réaliser ce que je veux faire ici?

merci.

+2

Vous paramétrez un membre de données * dans 'DoSomething'. Comment compile-t-il même? Et d'où vient «T» dans 'Strategy < T >'? –

Répondre

7

Il me semble que ce que vous voulez mettre en œuvre est un Policy-Based Design. Je ne suis pas sûr ce que Model et Strategy font exactement, mais il semble que Model est la classe racine, et Strategy est la classe Policy, que les utilisateurs voudraient fournir dans certains cas pour effectuer une manipulation spéciale. Il semble également que la seule raison pour laquelle vous gardez un pointeur sur l'objet Strategy<T> est que vous pouvez y appeler des fonctions.

Dans ce cas, vous pouvez concevoir votre classe comme ceci:

template<class Strategy> 
class Model : public Strategy { 
public: 
void DoSomething() 
{ 
    // magic happens (we will fill this in shortly) 
}; 
}; 

Vous appeler des méthodes sur la classe Strategy pour faire votre magie. En laissant les utilisateurs définir leur propre classe Strategy, vous leur donnez l'opportunité de définir leur propre "magie". Vous devez appliquer des règles sur la méthode que la classe Strategy fournira au minimum, afin que vous puissiez appeler ces méthodes dans Model. Par exemple, supposons que Model est en fait une sorte de gestionnaire de ressources, capable d'être un simple pointeur intelligent, ou quelque chose d'autre comme un gestionnaire de ressources pour une section critique de Windows. Renommons Model à auto_resource et Strategy deviendra release_policy et sera responsable de la libération de toute ressource qui lui a été affectée. Dans ce cas, vous pourriez avoir:

class pointer_release_policy 
{ 
public: 
    template<class Object> void release(Object* obj) { delete obj; } 
}; 

template<class Managed, class release_policy> 
class auto_resource : public release_policy 
{ 
public: 
    // ... ctors etc defined here 
    ~auto_resource() 
    { 
    release_policy::release(managed_); 
    } 
private: 
    Managed managed_; 
}; 

que vous pourriez utiliser pour std::string pointeurs comme ceci:

typedef auto_resource<std::string*, pointer_release_policy> string_ptr; 
string_ptr my_str; 

... et quand my_str tombe de la pile, la méthode release sera automatiquement appelé .

Plus tard, vous voulez ajouter une nouvelle politique pour la libération de Windows mutex HANDLE s:

class handle_release_policy 
{ 
public: 
    template<class Handle> void release(Handle h) 
    { 
    CloseHandle(h); // this is a WINAPI function that deallocates the specified resource 
    }; 
}; 

Vous pouvez utiliser ainsi:

typedef auto_resource<HANDLE, handle_resource_policy> handle_resource; 
//... allocate & use the mutex... 
handle_resource mutex = CreateMutex(0, 0, 0); 

Bien sûr, afin de chair tout ça vous besoin d'ajouter des fonctionnalités pour assigner, copier, libérer etc. les ressources. Voici un exemple de travail complet qui met tout ensemble. J'ai fourni 2 séries de politiques, l'une pour Windows CRITICAL_SECTION s, et un autre pour SOCKET s:

class SimpleCopyPolicy 
{ 
public: 
    template<class Resource> Resource copy(const Resource& rhs) const { Resource ret = rhs; return ret; } 
protected: 
    ~SimpleCopyPolicy(){}; 
}; 

class CritsecReleasePolicy 
{ 
public: 
    template<class Handle> bool release(Handle& h) 
    { 
     DeleteCriticalSection(&h); 
     return true; 
    } 
protected: 
    ~CritsecReleasePolicy() {}; 
}; 

class CritsecLockPolicy // CRITICAL_SECTION lock/unlock policies 
{ 
public: 
    template<class Handle> bool lock(Handle& h) 
    { 
     EnterCriticalSection(const_cast<CRITICAL_SECTION*>(&h)); 
     return true; 
    } 
    template<class Handle> bool unlock(Handle& h) 
    { 
     LeaveCriticalSection(&h); 
     return true; 
    } 
}; 


class SocketReleasePolicy 
{ 
public: 
    template<class Handle> bool release(Handle h) { return 0 != closesocket(h); } 
protected: 
    ~SocketReleasePolicy(){}; 
}; 

template<class Resource, typename ReleasePolicy, typename CopyPolicy = SimpleCopyPolicy> 
class simple_auto_resource : public ReleasePolicy, public CopyPolicy 
{ 
public: 
    typedef simple_auto_resource<Resource,ReleasePolicy,CopyPolicy> base_type; 

    simple_auto_resource() : res(0) {} 
    simple_auto_resource(const Resource & r) : res(copy(r)) {} 
    ~simple_auto_resource() { if(res) release(res); } 

    void clear() { if(res) release(res); res = 0; } 

    Resource& get() { return res; } 
    const Resource& get() const { return res; } 

    Resource detach() { Resource ret = res; res = 0; return ret; } 

    operator const Resource&() const { return get(); } 
    operator Resource&() { return get(); } 

    base_type& operator=(const Resource& rhs) { clear(); res = copy(rhs); return * this; } 

    template<class Comp> bool operator==(const Comp& rhs) const { return res == (Resource)rhs; } 
    template<class Comp> bool operator!=(const Comp& rhs) const { return res != (Resource)rhs; } 
    template<class Comp> bool operator<(const Comp& rhs) const { return res < (Resource)rhs; } 
private: 
    Resource res; 
}; 

typedef simple_auto_resource<CRITICAL_SECTION, CritsecReleasePolicy> auto_critsec; 
typedef simple_auto_resource<SOCKET,SocketReleasePolicy> auto_socket; 

Pour en savoir plus sur la conception basée sur des règles, voir Modern C++ Design.

...

+0

Merci pour une excellente réponse ... :) – SWKK

+0

@SWKK: Mon plaisir, profitez. –

+0

depuis plus de 2 ans .. mais c'est une bonne chose. Permettez-moi de vous poser cette question. En quoi le modèle _strategy_ diffère-t-il de _policy based pattern_? Une chose que je vois à partir de votre code est un thème de contextes variés, avec des algorithmes de stratégie très similaires. Les règles de version socket, pointeur et HANDLE fonctionnent toutes dans des contextes différents: _sockets pointers et Handles_. Le ** motif de design ** que je cherche est quand le contexte est constant, mais l'algorithme/protocal est unique. Donc, il y a peut-être deux distink qui ont besoin du contexte de ** policy pattern ** et des nombreux protocoles pour le ** pattern de stratégie ** – jaybny

0

Déplacez la fonction sur class Strategy<T>.

Questions connexes