模板重构
假设我们有多种类型的元素,并且我们想要创建一个 每种类型的“经理”。经理负责照顾 任何元素的创建、激活/停用和删除 (我们假设用户不会创建/销毁这些元素的实例 不使用管理器。 一个非常简单的代码示例如下:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您有多种选择,但基本思想是添加多态性。
对于运行时多态性,您有以下一般选择:
std::function
)您还可以使用编译时多态性,详见 Nim 的回答
例如,这里是模板方法:
You have any number of options, but the basic idea is to add polymorphism.
For runtime polymorphism, you have these general choices:
std::function
s)You could also use compile time polymorphism, as detailed in Nim's answer
For an example, here is template method:
具体从示例来看,除了提到的那 3 行之外,
class CachedElementMngr
似乎完全包含class NonCachedElementMngr
的所有功能。我会做这样的事情:而不是调用以下方法,
它应该被称为
Specifically from the example, it seems that
class CachedElementMngr
completely contains all the functionality ofclass NonCachedElementMngr
except those 3 lines mentioned. I would do something like this:And intead of calling following method as,
it should be called as
将特定行为分解为策略:
Factor out that specific behavior into a policy:
使用策略类...
然后定义两个策略,都实现
init()
、cleanup()
和release()
方法,但是一个执行额外的行,另一个不执行...编辑:修复了我的伪代码,使其更像真实代码,我想表明您可以使
Policy
依赖于T
也是如此,然后使用例如针对特定T
的专门化,或者您不必这样做 - 您可以决定如何定义策略......Use a policy class...
Then define two policies, both implementing the
init()
,cleanup()
andrelease()
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 onT
too, and then use for example specialization for specificT
, or you don't have to - you can decide how to define the policy....