基本用途編程
boost::function就像C#裏的delegate,能夠指向任何函數,包括成員函數。當用bind把某個成員函數綁到某個對象上時,咱們獲得了一個closure(閉包)。例如:設計模式
class Foo { public: void methodA(); void methodInt(int a); }; class Bar { public: void methodB(); }; boost::function<void()> f1; // 無參數,無返回值 Foo foo; f1 = boost::bind(&Foo::methodA, &foo); f1(); // 調用 foo.methodA(); Bar bar; f1 = boost::bind(&Bar::methodB, &bar); f1(); // 調用 bar.methodB(); f1 = boost::bind(&Foo::methodInt, &foo, 42); f1(); // 調用 foo.methodInt(42); boost::function<void(int)> f2; // int 參數,無返回值 f2 = boost::bind(&Foo::methodInt, &foo, _1); f2(53); // 調用 foo.methodInt(53);
若是沒有boost::bind,那麼boost::function就什麼都不是,而有了bind(),「同一個類的不一樣對象能夠delegate給不一樣的實現,從而實現不一樣的行爲」(myan語),簡直就無敵了。閉包
對程序庫的影響編程語言
程序庫的設計不該該給使用者帶來沒必要要的限制(耦合),而繼承是僅次於最強的一種耦合(最強耦合的是友元)。若是一個程序庫限制其使用者必須從某個class派生,那麼我以爲這是一個糟糕的設計。不巧的是,目前有些程序庫就是這麼作的。函數
例1:線程庫工具
常規OO設計: 線程
寫一個Thread base class,含有(純)虛函數 Thread#run(),而後應用程序派生一個繼承class,覆寫run()。程序裏的每一種線程對應一個Thread的派生類。例如Java的Thread能夠這麼用。設計
缺點:若是一個class的三個method須要在三個不一樣的線程中執行,就得寫helper class(es)並玩一些OO把戲。code
基於closure的設計: 面向對象設計模式
令Thread是一個具體類,其構造函數接受Callable對象。應用程序只需提供一個Callable對象,建立一份Thread實體,調用Thread#start()便可。Java的Thread也能夠這麼用,傳入一個Runnable對象。C#的Thread只支持這一種用法,構造函數的參數是delegate ThreadStart。boost::thread也只支持這種用法。
// 一個基於 closure 的 Thread class 基本結構 class Thread { public: typedef boost::function<void()> ThreadCallback; Thread(ThreadCallback cb) : cb_(cb) { } void start() { /* some magic to call run() in new created thread */ } private: void run() { cb_(); } ThreadCallback cb_; // ... }; 使用: class Foo { public: void runInThread(); }; Foo foo; Thread thread(boost::bind(&Foo::runInThread, &foo)); thread.start();
對面向對象程序設計的影響
一直以來,我對面向對象有一種厭惡感,疊牀架屋,繞來繞去的,一拳拳打在棉花上,不解決實際問題。面向對象三要素是封裝、繼承和多態。我認爲封裝是根本的,繼承和多態則是無關緊要。用class來表示concept,這是根本的;至於繼承和多態,其耦合性太強,每每不划算。
繼承和多態不只規定了函數的名稱、參數、返回類型,還規定了類的繼承關係。在現代的OO編程語言裏,藉助反射和attribute/annotation,已經大大放寬了限制。舉例來講,JUnit 3.x 是用反射,找出派生類裏的名字符合 void test*() 的函數來執行,這裏就沒繼承什麼事,只是對函數的名稱有部分限制(繼承是全面限制,一字不差)。至於JUnit 4.x 和 NUnit 2.x 則更進一步,以annoatation/attribute來標明test case,更沒繼承什麼事了。
個人猜想是,當初提出面向對象的時候,closure尚未一個通用的實現,因此它沒能算做基本的抽象工具之一。如今既然closure已經這麼方便了,或許咱們應該從新審視面向對象設計,至少不要那麼濫用繼承。
自從找到了boost::function+boost::bind這對神兵利器,不用再考慮類直接的繼承關係,只須要基於對象的設計(object-based),拳拳到肉,程序寫起來頓時順手了不少。
對面向對象設計模式的影響
既然虛函數能用closure代替,那麼不少OO設計模式,尤爲是行爲模式,失去了存在的必要。另外,既然沒有繼承體系,那麼建立型模式彷佛也沒啥用了。
最明顯的是Strategy,不用累贅的Strategy基類和ConcreteStrategyA、ConcreteStrategyB等派生類,一個boost::function<>成員就解決問題。在《設計模式》這本書提到了23個模式,我認爲iterator有用(或許再加個State),其餘都在擺譜,拉虛架子,沒啥用。或許它們解決了面向對象中的常見問題,不過要是個人程序裏連面向對象(指繼承和多態)都不用,那彷佛也不用叨擾面向對象設計模式了。
或許closure-based programming將做爲一種新的programming paradiam而流行起來。