2011-07-08 46 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的概念之前:和之后:方法之后特别如此。 –