標籤:style blog http color 使用 os 資料 io
在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,提供了效能最佳化。