Рефакторинг шаблона - PullRequest
       6

Рефакторинг шаблона

1 голос
/ 21 апреля 2011

Давайте представим, что у нас есть несколько типов элементов, и мы хотим создать «менеджера» для каждого из них.Менеджер позаботится о создании, активации / деактивации и удалении любого из элементов (мы предполагаем, что пользователь не будет создавать / уничтожать экземпляры этих элементов без использования менеджера. Очень простой пример кода будет выглядеть примерно так::

template <class T>
class NonCachedElementMngr
{
public:
    NonCachedElementMngr():
        rmCounter(0)
    {}

    ~ NonCachedElementMngr()
    {
        T* element = 0;
        if(mElements.size() > 0)
        {
            typename std::set<T*>::iterator it;
            for(it = mElements.begin(); it != mElements.end(); ++it)
            {
                element = *it;
                element->deactivate();
                delete element;
            }
        }
    }

    T* create()
    {
        T* element = new T();
        element->activate();
        mElements.insert(element);
        return element;
    }

    bool release(T* element)
    {
        bool ret = false;
        typename std::set<T*>::iterator it;
        it = mElements.find(element);
        if(it != mElements.end())
        {
            element->deactivate();
            delete element;
            mElements.erase(it);
            ret = true;
        }
        return ret;
    }

private:

    std::set<T*> mElements;
    int rmCounter;
};

Давайте теперь представим, что для подгруппы объектов, кроме основной операции, нам также необходимо выполнить некоторое кэширование. Для этой подгруппы типов мы могли бы определить другого «менеджера», например:

template <class T>
class CachedElementMngr
{
public:
    CachedElementMngr():
        rmCounter(0)
    {}

    ~CachedElementMngr()
    {
        T* element = 0;
        if(mElements.size() > 0)
        {
            typename std::set<T*>::iterator it;
            for(it = mElements.begin(); it != mElements.end(); ++it)
            {
                element = *it;
                element->removeFromCache();  // <<<<<<<<<<<<<< Different line
                element->deactivate();
                delete element;
            }
        }
    }

    T* create()
            {
        T* element = new T();
        element->storeInCache(); // <<<<<<<<<<<<<< Different line
        element->activate();
        mElements.insert(element);
        return element;
            }

    bool release(T* element)
    {
        bool ret = false;
        typename std::set<T*>::iterator it;
        it = mElements.find(element);
        if(it != mElements.end())
        {
            element->removeFromCache();  // <<<<<<<<<<<<<< Different line
            element->deactivate();
            delete element;
            mElements.erase(it);
            ret = true;
        }
        return ret;
    }

private:

    std::set<T*> mElements;
    int rmCounter;
};

Очевидно, что оба менеджера абсолютно одинаковы, за исключением трех строк, помеченных как таковые. Как я могу реорганизовать эти два шаблона?нет. Обратите внимание, что в деструкторе также есть другая строка. Любое возможное предложение (виртуальное наследование, специализация шаблона, SFINAE ...) будет очень приветствоваться.

Ответы [ 4 ]

2 голосов
/ 21 апреля 2011

Включите это конкретное поведение в политику:

#include <set>

struct cached_tag;
struct noncached_tag;

template<typename Tag>
struct ElementMngrCachePolicy;

template<>
struct ElementMngrCachePolicy<cached_tag>
{
    template<typename T>
    static void removeFromCache(T* const element) { /*impl...*/ }

    template<typename T>
    static void storeInCache(T* const element) { /*impl...*/ }
};

template<>
struct ElementMngrCachePolicy<noncached_tag>
{
    template<typename T>
    static void removeFromCache(T* const) { /*do nothing*/ }

    template<typename T>
    static void storeInCache(T* const) { /*do nothing*/ }
};

template<typename T, typename CachePolicy>
class ElementMngr
{
    typedef std::set<T*> elements_t;

public:
    ElementMngr() :
        rmCounter()
    { }

    ~ElementMngr()
    {
        for (typename elements_t::iterator it = mElements.begin(); it != mElements.end(); ++it)
        {
            T* const element = *it;
            CachePolicy::removeFromCache(element);
            element->deactivate();
            delete element;
        }
    }

    T* create()
    {
        T* const element = new T();
        CachePolicy::storeInCache(element);
        element->activate();
        mElements.insert(element);
        return element;
    }

    bool release(T* const element)
    {
        typename elements_t::iterator it = mElements.find(element);
        if (it == mElements.end())
            return false;

        CachePolicy::removeFromCache(element);
        element->deactivate();
        delete element;
        mElements.erase(it);
        return true;
    }

private:
    elements_t mElements;
    int rmCounter;
};

template<typename T>
class CachedElementMngr : public ElementMngr<T, ElementMngrCachePolicy<cached_tag> >
{ };

template<typename T>
class NonCachedElementMngr : public ElementMngr<T, ElementMngrCachePolicy<noncached_tag> >
{ };
1 голос
/ 21 апреля 2011

Использовать класс политики ...

template <class T, typename Policy>
class ElementMngr
{
    ~ElementMngr()
    {
        T* element = 0;
        if(mElements.size() > 0)
        {
            typename std::set<T*>::iterator it;
            for(it = mElements.begin(); it != mElements.end(); ++it)
            {
                element = *it;
                Policy::cleanup(element);
                delete element;
            }
        }
    }

    T* create()
            {
        T* element = new T();
        Policy::init(element);
        mElements.insert(element);
        return element;
            }

    bool release(T* element)
    {
        bool ret = false;
        typename std::set<T*>::iterator it;
        it = mElements.find(element);
        if(it != mElements.end())
        {
            Policy::release(element);
            delete element;
            mElements.erase(it);
            ret = true;
        }
        return ret;
    }
};

Затем определите две политики, каждая из которых реализует методы init(), cleanup() и release(), но одна выполняет дополнительную строку, а другая - нет ...

РЕДАКТИРОВАТЬ: исправил мой псевдокод так, чтобы он больше походил на реальный код, я хотел показать, что вы можете сделать Policy зависимым также от T, а затем использовать, например, специализацию для конкретного T, или вы не не нужно - вы можете решить, как определить политику ....

0 голосов
/ 21 апреля 2011

В частности, из примера кажется, что class CachedElementMngr полностью содержит все функциональные возможности class NonCachedElementMngr, за исключением тех трех упомянутых строк.Я хотел бы сделать что-то вроде этого:

template<typename T>
class NonCachedElementMngr
{
  /* put all content of "CachedElementMngr" in your example and below methods */
  virtual void removeFromCache(T* p) { /* empty */ }
  virtual void storeInCache(T* p) { /* empty */ }
  virtual ~NonCachedElementMngr() { /*retain original */ }
};

template<typename T>
class CachedElementMngr : public NonCachedElementMngr<T>
{
// everything is inherited; just add below methods in this class
  virtual void removeFromCache(T* p) { p->removeFromCache(); }
  virtual void storeInCache(T* p) { p->storeInCache(); }
  virtual ~CachedElementMngr() { /*retain original */ }
};

И для вызова следующего метода, как,

p->removeFromCache();
p->storeInCache();

он должен называться

removeFromCache(p);
storeInCache(p);
0 голосов
/ 21 апреля 2011

У вас есть любое количество вариантов, но основная идея - добавить полиморфизм.

Для полиморфизма во время выполнения у вас есть следующие общие варианты:

  • Шаблон стратегии
  • Хранить функторы, определяющие, использовать ли кэш или нет (т. Е. С std::function s)
  • Шаблонный метод (который не имеет ничего общего с шаблонами C ++)

Вы также можете использовать полиморфизм времени компиляции, как описано в ответ Нима

Для примера, вот метод шаблона:

template <typename T>
class ElementManager
{
    std::set<T*> mElements;
    int rmCounter;
    virtual void CacheStore(T& element) = 0;
    virtual void CacheRemove(T& element) = 0;
public:
    ElementManager():
        rmCounter(0)
    {}

    ~ElementManager()
    {
        T* element = 0;
        if(mElements.size() > 0)
        {
            typename std::set<T*>::iterator it;
            for(it = mElements.begin(); it != mElements.end(); ++it)
            {
                element = *it;
                CacheRemove(element);
                element->deactivate();
                delete element;
            }
        }
    }

    T* create()
            {
        T* element = new T();
        CacheStore(element);
        element->activate();
        mElements.insert(element);
        return element;
            }

    bool release(T* element)
    {
        bool ret = false;
        typename std::set<T*>::iterator it;
        it = mElements.find(element);
        if(it != mElements.end())
        {
            CacheRemove(element);
            element->deactivate();
            delete element;
            mElements.erase(it);
            ret = true;
        }
        return ret;
    }
}

template <class T>
class CachedElementMngr : public ElementManager<T>
{
    virtual void CacheStore(T& element)
    {
        element->storeInCache();
    }

    virtual void CacheRemove(T& element)
    {
        element->removeFromCache();
    }
};

template <class T>
class NonCachedElementMngr : public ElementManager<T>
{
    virtual void CacheStore(T& element)
    {
        //Purposely Empty
    }

    virtual void CacheRemove(T& element)
    {
        //Purposely Empty
    }
};
...