模板重构

发布于 2024-11-02 19:12:06 字数 2850 浏览 0 评论 0原文

假设我们有多种类型的元素,并且我们想要创建一个 每种类型的“经理”。经理负责照顾 任何元素的创建、激活/停用和删除 (我们假设用户不会创建/销毁这些元素的实例 不使用管理器。 一个非常简单的代码示例如下:

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...)都将受到欢迎。

Let's imagine we have several type of elements, and we want to create a
'manager' for every type of them. The manager takes care of the
creation, activation/deactivation and removal for any of the elements
(we assume the user will no create/destroy the instances of these elements
without using the manager.
A very simple example of the code would be something like this:

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;
};

Let's imagine now that, for a subgroup of objects,
apart from the basic operation, we need also to do
some caching. For that subgroup of types, we could
define another 'manager' like this:

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;
};

As obvious, both managers are exactly the same, except for the
three lines marked as so.
How could I refactor this two templates?
We know at compile time if a specific type will be cacheable or not.
Notice there is also a different line in the destructor.
Any feasible proposal (virtual inheritance, template specialization, SFINAE...) would be very welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

落在眉间の轻吻 2024-11-09 19:12:07

您有多种选择,但基本思想是添加多态性。

对于运行时多态性,您有以下一般选择:

  • 策略模式
  • 存储函子定义是否使用缓存(即使用 std::function
  • 模板方法(与 C++ 模板无关)

您还可以使用编译时多态性,详见 Nim 的回答

例如,这里是模板方法:

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
    }
};

You have any number of options, but the basic idea is to add polymorphism.

For runtime polymorphism, you have these general choices:

  • Strategy Pattern
  • Store functors defining whether or not to use the cache (i.e. with std::function s)
  • Template method (which has nothing to do with C++ templates)

You could also use compile time polymorphism, as detailed in Nim's answer

For an example, here is template method:

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
    }
};
山田美奈子 2024-11-09 19:12:07

具体从示例来看,除了提到的那 3 行之外,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);

Specifically from the example, it seems that class CachedElementMngr completely contains all the functionality of class NonCachedElementMngr except those 3 lines mentioned. I would do something like this:

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 */ }
};

And intead of calling following method as,

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

it should be called as

removeFromCache(p);
storeInCache(p);
記柔刀 2024-11-09 19:12:06

将特定行为分解为策略:

#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> >
{ };

Factor out that specific behavior into a policy:

#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> >
{ };
影子是时光的心 2024-11-09 19:12:06

使用策略类...

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 的专门化,或者您不必这样做 - 您可以决定如何定义策略......

Use a policy class...

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;
    }
};

Then define two policies, both implementing the init(), cleanup() and release() methods, but one does the extra line, the other doesn't...

EDIT: fixed my pseudo code so that it's more like real code, I wanted to show that you can make Policy depend on T too, and then use for example specialization for specific T, or you don't have to - you can decide how to define the policy....

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文