2011-07-08 70 views
2

CLOS有一個簡潔的概念:before,:after和:around方法。CLOS:在C++之前,之後和之後:在C++中圍繞

  • The:before方法在主方法之前被調用。
  • :在主方法之後調用方法之後。
  • :around方法被調用:before + primary +:after序列。

The:before,:after,and:around方法被鏈接而不是被覆蓋。假設父類和子類都定義了一個foo方法和:before foo方法。孩子的foo方法覆蓋父母的foo方法,但是孩子和父母的方法:在調用此重寫的方法之前調用foo方法之前。

Python裝飾器提供了類似於CLOS的方法:around方法。在C++中沒有這樣的東西。它必須是手工卷制:

class Child : public Parent { 
    virtual void do_something (Elided arguments) { 
     do_some_preliminary_stuff(); 
     Parent::do_something (arguments); 
     do_some_followup_stuff(); 
    } 
}; 

缺點:

  • 這是一個反模式的一些人。
  • 它需要我明確指定父類。
  • 它需要我的類的擴展符遵循相同的範例。
  • 如果我需要調用祖父母因爲父母不覆蓋do_something,那麼多重繼承又該如何呢?
  • 它並不完全捕捉到CLOS概念。

當我使用Flavors(CLOS的前身)時,我發現這些概念非常方便。我已經在一些地方使用了上述解決方法,並且有少數人將其作爲反模式提出質疑。 (其他人已經在其他地方效仿了,所以嘲笑不是通用的。)

問題:這是否被認爲是C++中的反模式,是否有更好的解決方法?

回答

1

這是我可以做的,但它仍然有點難看。

基本上我把實際的工作放在一個單獨的鉤子中,所以你沒有在處理方法中調用前/後鉤子。在繼承鏈中,您可以完全控制是否要添加前/後鉤子以及鉤子調用的順序(在子進程之前或之後調用父進程的鉤子)。

#include <iostream> 
#define C(s) std::cout << s << std::endl; 

class Parent { 
    public: 
    virtual void do_something(int arg) { 
     do_some_pre_hook(); 
     do_some_hook(arg); 
     do_some_post_hook(); 
    } 
    virtual void do_some_pre_hook() { 
     C("parent pre_hook"); 
    } 
    virtual void do_some_post_hook() { 
     C("parent post_hook"); 
    } 
    virtual void do_some_hook(int arg) { 
     //this is where you actually do the work 
    } 
}; 

class Child : public Parent { 
    public: 
    typedef Parent super; 

    virtual void do_some_pre_hook() { 
     super::do_some_pre_hook(); 
     C("Child pre_hook"); 
    } 
    virtual void do_some_post_hook() { 
     super::do_some_post_hook(); 
     C("Child post_hook"); 
    } 
}; 

class GrandChild : public Child { 
    public: 
    typedef Child super; 

    virtual void do_some_pre_hook() { 
     super::do_some_pre_hook(); 
     C("GrandChild pre_hook"); 
    } 
    virtual void do_some_post_hook() { 
     super::do_some_post_hook(); 
     C("GrandChild post_hook"); 
    } 
    virtual void do_some_hook(int arg) { 
     //this is where you actually do the work 
     C("GrandChild hook"); 
    } 
}; 

int main() { 
    GrandChild gc; 
    gc.do_something(12); 
} 

注意:理想情況下,你會使用AOP C++編譯器編譯或擴展這樣的任務,但我最後一次嘗試它,它不太穩定...

+0

不錯。你做了do_something虛擬意味着一個派生類可以覆蓋它,如果它通過調用super :: do_something來實現,那麼CLOS也是:around。總而言之,雖然這個語言不支持它,但是任何實現都會變得有點笨拙。 –

1

我不是說這相當於或相當於其他什麼語言做的,但我認爲非虛擬接口慣用法適用於您的問題:

class parent { 
public: 
    void foo() 
    { 
     before_foo(); 
     do_foo(); 
     after_foo(); 
    } 

protected: 
    // you can make those pure virtual with an implentation, too 
    virtual void before_foo() { ... } 
    virtual void do_foo() { ... } 
    virtual void after_foo() { ... } 
}; 

class child: public parent { 
protected: // or private 
    void before_foo() { ... } 
    void do_foo() { ... } 
    // must provide a dummy after_foo that delegates to parent::after_foo 
    // if it is pure virtual in the parent class 
}; 

當調用p.foo(),最衍生before_foo,總是調用after_foodo_foo

+0

我也許太簡化了這個例子。我有一個多層次的繼承,每個派生類執行調用父類的東西。不過,NVI是一種可能性。仍然kludgy因爲它仍然需要派生類來調用父方法。爲了使before_foo和after_foo正確,派生類必須調用父類before_或after_方法。 –

+0

我碰到了你的答案和yi_H's。 yi_H的答案更接近模擬CLOS包裝方法的味道,但我確實喜歡NVI的想法。當我(如果)將來遇到對CLOS概念的需求時,我會記住這兩個建議。謝謝。 –

+0

@大衛我錯過了關於鏈接的那一點,對不起。 –

2

你可以很好地使用(std/boost)::shared_ptr得到這個基礎知識。有關詳細信息,請參閱此處:http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/sp_techniques.html#wrapper

獲取您提到的繼承行爲只需要前綴/後綴函數來調用父類中的前綴/後綴函數。

+0

有趣的是,自從Stroustrup(簡要地)接受了實現CLOS的概念之前:和之後:方法之後特別如此。 –