我有以下类架构:
Bar
在我的代码的另一部分中,我有一个函数,它遍历一个动物列表,并且需要在几个派生类的情况下执行特殊操作,否则需要执行默认操作。考虑到以下限制,我如何优雅地处理这种情况:
答案 0 :(得分:3)
这是一种方式 - 使用概念模型习语(我的名字):
IEnumerable<Program> records = reader.GetRecords<Program>().Where(r => r.Age >= 62).ToList();
writer.WriteRecords(records);
// No futher code
预期产出:
#include <iostream>
#include <vector>
struct AnimalConcept {
virtual ~AnimalConcept() = default;
virtual void make_noise() const = 0;
};
// default case
void make_noise_for(const AnimalConcept&)
{
std::cout << "no noise" << std::endl;
}
template<class Model>
struct AnimalModel : AnimalConcept
{
void make_noise() const override {
make_noise_for(static_cast<const Model&>(*this));
}
};
// some models
struct Cat : AnimalModel<Cat>
{
};
struct Dog : AnimalModel<Dog>
{
};
struct Giraffe : AnimalModel<Giraffe>
{
};
// separation of concerns - specific overrides
void make_noise_for(const Cat&) {
std::cout << "meow\n";
}
void make_noise_for(const Dog&) {
std::cout << "woof\n";
}
// test
using namespace std;
int main(){
std::vector<std::unique_ptr<const AnimalConcept>> animals;
animals.emplace_back(new Cat);
animals.emplace_back(new Dog);
animals.emplace_back(new Giraffe);
for (const auto& p : animals) {
p->make_noise();
}
return 0;
}
这是实现它的另一种方式(这个更好,因为它允许所有动物拥有不相关的接口):
meow
woof
no noise
预期产出:
#include <iostream>
#include <vector>
struct AnimalConcept {
virtual ~AnimalConcept() = default;
virtual void make_noise() const = 0;
};
// default case
template<class T>
void make_noise_for(const T&)
{
std::cout << "this animal makes no noise" << std::endl;
}
template<class Model>
struct AnimalModel : AnimalConcept
{
template<class...Args>
AnimalModel(Args&&...args)
: _model { std::forward<Args>(args)... }
{}
private:
void make_noise() const override {
make_noise_for(_model);
}
Model _model;
};
// some models
struct Cat
{
Cat(std::string name)
: _name { std::move(name) }
{}
const std::string& name() const {
return _name;
}
private:
std::string _name;
};
struct Dog
{
Dog(std::string name, int age)
: _name { std::move(name) }
, _age { age }
{}
const std::string& name() const {
return _name;
}
int age() const {
return _age;
}
private:
std::string _name;
int _age;
};
struct Giraffe
{
};
// separation of concerns - specific overrides
void make_noise_for(const Cat& c) {
std::cout << c.name() << " says meow\n";
}
void make_noise_for(const Dog& d) {
std::cout << "the dog called " << d.name() << " who is " << d.age() << " years old says woof\n";
}
// test
using namespace std;
int main(){
std::vector<std::unique_ptr<const AnimalConcept>> animals;
animals.emplace_back(new AnimalModel<Cat> { "felix" });
animals.emplace_back(new AnimalModel<Dog> { "fido", 2 });
animals.emplace_back(new AnimalModel<Giraffe>);
for (const auto& p : animals) {
p->make_noise();
}
return 0;
}
答案 1 :(得分:0)
您可以使用以下组合来获取基于类型的调度。
// Implement this function in a .cpp file.
int getNextTypeID()
{
static int typeID = 0;
return ++typeID;
}
class Animal
{
virtual int getTypeID();
};
class Cat : public Animal
{
static int getID()
{
static int typeID = getNextTypeID();
}
virtual int getTypeID()
{
return getID();
}
};
class Dog : public Animal
{
static int getID()
{
static int typeID = getNextTypeID();
}
virtual int getTypeID()
{
return getID();
}
};
foo.h中:
typedef void (*AnimalFunction)(Animal& a);
int registerAnimalFunctor(int typeID, AnimalFunction f);
void foo(Animal& a);
Foo.cpp中:
typedef std::map<int, AnimalFunction> AnimalFunctionMap;
AnimalFunctionMap& getAnimalFunctionMap()
{
static AnimalFunctionMap theMap;
return theMap;
}
int registerAnimalFunctor(int typeID, AnimalFunction f)
{
getAnimalFunctionMap()[typeID] = f;
return 0;
}
void defaultAnimalFunction(a)
{
// Default action
}
void foo(Animal& a)
{
AnimalFunctionMap& theMap = getAnimalFunctionMap();
AnimalFunctionMap::iterator iter = theMap.find(a.getTypeID());
if ( iter != theMap.end() )
{
iter->second(a);
}
else
{
defaultAnimalFunction(a);
}
}
cat_foo.cpp:
void CatFunction(Animal& a)
{
// Cat action.
}
int dummy = registerAnimalFunctor(Cat::getID(), CatFunction);
dog_foo.cpp:
void DogFunction(Animal& a)
{
// Dog action.
}
int dummy = registerAnimalFunctor(Dog::getID(), DogFunction);