在C++中,一般的架构设计都是基于多态,基于接口编程。一般的是基类提供接口,子类根据具体的业务逻辑来实现接口,以此来提供程序设计的过程中的可注入性,提高灵活性,但是事实可能不经如此。引入了多态,其实也就是引入了耦合,其强制规定了继承基类和接口的形式,这在整体继承体系中都是不可以更改的。
如下例子:
#ifndef ANIMAL_H
#define ANIMAL_H
#include <iostream>
class Animal
{
public:
virtual void eat() = 0;
virtual ~Animal() = 0;
};
Animal::~Animal()
{
}
class Dog : public Animal
{
public:
void eat()
{
std::cout << "DOG EAT MEAT!\n";
}
~Dog() {}
};
class Cat : public Animal
{
public:
void eat()
{
std::cout << "CAT EAT FISH!\n";
}
~Cat() {}
};
class Pet
{
public:
Pet(Animal *_a) : _aAnimal(_a)
{
}
~Pet()
{
delete _aAnimal;
}
void eat()
{
_aAnimal->eat();
}
private:
Animal *_aAnimal;
};
#endif // ANIMAL_H
在main函数中
Dog *dog = new Dog();
Pet p(dog);
p.eat();
Cat *cat = new Cat();
Pet p1(cat);
p1.eat();
我们通过多态实现了注入,这就和具体类型和函数形式相关,可是如果现在,我们的eat还是多了个参数Foot,那么我们只有改源码,可以改动eat函数原型,或则在添加一个eat函数,这就是多态引入的耦合。
C++0x中的bind函数和function模板类为我们提供了很好的设计解决方案,提供多态基于函数对象,其只和函数的返回值和参数有关。陈硕老师称其为Object-base编程,通过对象来完成功能注入。拳拳到肉(陈老师原话)。关于bind和function不清楚的可以先了解下,这边就不赘述了。
看这个例子:
#include <iostream>
#include <functional>
#include <list>
class Work
{
public:
typedef std::function<void ()> doAmessage;
void registerDoMessage(const doAmessage &f)
{
_aAmessage = f;
}
void do_aTask()
{
std::cout << "DO A TASK!\n";
}
void aMessageComing()
{
_aAmessage();
}
private:
doAmessage _aAmessage;
};
class Logic
{
public:
typedef std::function<void ()> doTaskCallBack;
Logic(const doTaskCallBack &f) : _dtask(f)
{
}
void make()
{
_dtask();
}
void aMessageComing()
{
_messageList.push_back("A TASK!");
}
void printMessages()
{
for (auto ite = _messageList.begin(); ite != _messageList.end(); ++ite)
{
std::cout << (*ite).data() << '\n';
}
}
private:
doTaskCallBack _dtask;
std::list<std::string> _messageList;
};
int main()
{
//std::cout << "Hello World\n";
Work worker;
Logic logic(std::bind(&Work::do_aTask,worker));
worker.registerDoMessage(std::bind(&Logic::aMessageComing,&logic));
logic.make();
worker.aMessageComing();
worker.aMessageComing();
logic.printMessages();
return 0;
}
work是一个底层的操作类,Logic是一个与业务相关的类,work为Logic提供一些底层服务,logic知道work对底层数据的操作。
Work::do_aTask为Logic提供服务,在Logic::make中调用,在
Logic logic(std::bind(&Work::do_aTask,worker));
这句完成了注入操作。
Work::aMessageComing,需要Logic提供的具体的逻辑,在
worker.registerDoMessage(std::bind(&Logic::aMessageComing,&logic));
完成了注入。
如果现在我定义了一个class,其只用提供一个void xxx()成员函数,那就可以直接绑定给Work使用。
可以注意到我的回调对象都是
std::function<void ()>
为什么,我是想在提供一些变化。
比如,
Logic的
aMessageComing(Parameter *_p)
函数添加了一个函数,我希望Work不需要改变,这就需要一个参数,使用对象来实现对参数的保存。
定义如下:
struct Parameter
{
Parameter() :
para1(0),
para2(0)
{
}
int para1;
int para2;
};
使用如下:
Parameter para;
Work worker;
Logic logic(std::bind(&Work::do_aTask,worker));
worker.registerDoMessage(std::bind(&Logic::aMessageComing,&logic,¶));
logic.make();
worker.aMessageComing();
para.para2 += 10;
worker.aMessageComing();
logic.printMessages();
运行结果:
这样可以不改变Work,多引入一个参数,可是却也映入了一个全局的变量,在需要的使用需要改变参数,因为可能我们不知道函数的调用时间,我们需要做随时改变。
这样我们就不需要通过继承完成注入,使用function和bind需要通过对象来完成注入。
不使用继承虚函数使用函数对象可以获得运行时的效率,函数对象可以被inline,提供了性能优化。