【发布时间】:2018-01-10 20:09:28
【问题描述】:
为了演示,假设我有一些动物类,每个类都派生自一个“动物”类,每个“知道”它们是什么类型,并且每个都具有某种独特的能力:
enum class animal_type { antelope, bear, cat };
class animal
{
};
class antelope : public animal
{
public:
static const animal_type type = animal_type::antelope;
void run() { std::cout << "antelope runs\n"; };
};
class bear : public animal
{
public:
static const animal_type type = animal_type::bear;
void roar() { std::cout << "bear roars\n"; };
};
class cat : public animal
{
public:
static const animal_type type = animal_type::cat;
void meow() { std::cout << "cat meows\n"; };
};
现在,我希望能够根据动物的类型检索动物:
class animal_getter
{
public:
animal& get(animal_type t)
{
static antelope s_antelope;
static bear s_bear;
static cat s_cat;
switch (t)
{
case animal_type::antelope:
return s_antelope;
case animal_type::bear:
return s_bear;
case animal_type::cat:
return s_cat;
}
}
};
最后,返回动物的实际类型,让调用语法更好:
template<typename T>
T& get()
{
return static_cast<T&>(get(T::type));
}
现在我可以这样写了:
animal_getter ag;
ag.get<antelope>().run();
而不是冗长的:
animal_getter ag;
static_cast<antelope&>(ag.get(animal_type::antelope)).run();
我希望这没有什么太不合理的地方。但是现在我希望能够对获取动物进行单元测试,因此理想情况下可以伪造 animal_getter 类(想象实际实现访问数据库或您在单元测试中不想要的东西,因此是假的) .因此,最好为“animal getter”类定义一个接口,然后创建一个实现该接口的假货。那么问题来了,这个界面可以写吗?这不会编译:
struct IAnimalGetter
{
virtual template<typename T> T& get() = 0;
};
是否有可能挽救这个想法或者模板函数永远不能被声明为虚拟函数来定义包含它们的接口?
如果这个想法是行不通的,这是从什么时候开始出错的?是在编写模板函数时,它自己进行了转换?我是否应该在返回动物对象的东西上停下来,然后让调用者负责演员表?
【问题讨论】:
-
不清楚你为什么需要这个。只需公开您的静态数据成员即可。
ag.get<antelope>().run()什么都不做ag.s_antelope().run()不做。 -
(续)没有虚拟模板,所以虚拟
get template是不可能的。您可以通过访问者模式和/或 ctrategicalky 放置的演员来解决这个问题,但仍然不清楚您到底想要实现什么。get<antelope>()买给你什么get_antelope()买不到? -
如果派生类没有任何共同点,也许错误是基类为空?需要强制转换或切换类型都是模型存在问题的迹象。羚羊当然不会沉默,熊会跑。
-
谢谢大家。我正在处理的实际代码远比这复杂得多——为了简洁起见,我只是想尽可能简单地演示这个问题。请不要将此示例代码视为我正在尝试做的事情的说明,即专门用于基于接口创建假对象。
标签: c++ unit-testing templates interface