65.9K
CodeProject 正在变化。 阅读更多。
Home

构建装饰器链

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (4投票s)

2010年10月22日

CPOL

3分钟阅读

viewsIcon

31474

downloadIcon

152

本文介绍了一种方法,如何以通用的方式构建灵活且可扩展的装饰器链。 如果它与 boost::factory 和 boost::bind 结合使用,则其威力将得到最佳体现。

引言

装饰器模式是现代面向对象开发中的一种经典设计模式。 通常,它用于扩展现有对象的功能。 在本文中,我将介绍一种使用现代 C++ 构建灵活且可扩展的装饰器链的简单方法。

该方法大量使用了 boost::factory 模板。 结合 boost::bindboost::ref,它是一种强大且易于使用的工具,用于创建装饰器链。

我将在此处展示的代码是关于如何管理和创建此类链的基本想法。 我为特定的应用程序开发了它,并且我开始喜欢它。 然而,这只是一种可能的方式。 如果您有不同的方法,请告诉我。 当然,非常欢迎讨论。

装饰器

装饰器链通常使用装饰器的构造函数参数构建

#include <iostream>

using namespace std;

class abstract_class
{
public:
    virtual ~abstract_class( void ) { }
    virtual void do_something( void ) = 0;
};

class concrete_class : public abstract_class
{
public:
    void do_something( void ) { cout << "concrete_class_a\n"; }
};

class decorator : public abstract_class
{
    abstract_class *m_decorated_class;
public:
    decorator( abstract_class *decorated_class ) : 
		m_decorated_class( decorated_class ) { }
    void do_something( void )
    {
        m_decorated_class->do_something();
        cout << "decorator_a\n";
    }
};

int main( int argc , char **argv )
{
    concrete_class c;
    decorator d( &c );
    d.do_something();

    return 0;
}

请注意,应该被装饰的组件作为指针传递,并且该对象的销毁不由装饰器处理。 当然,也可以将组件作为引用传递,或者将组件的所有权交给装饰器。 但是,您必须记住头元素,即链中的最后一个装饰器,它用于调用 do_something

int main( int argc , char **argv )
{
    concrete_class c;     // c is the head element
    decorator d1( &c );   // d1 is now the head element
    decorator d2( &d1 );  // d2 is now the head element
    decorator d3( &d2 );  // d3 is now the head element
    d3.do_something();    // use the head element to do_something

    return 0;
}

请记住,在上面的示例中,头元素很简单。 但在其他应用中可能会变得很麻烦。 例如,如果装饰器链隐藏在其他一些类中,或者您必须从某些配置文件中动态构建链,等等。这就是 decorator_chain 发挥作用的地方。

装饰器链

主要思想是将装饰器对象的创建和实例化以及被装饰的组件封装到一个类中

template< class Class , class DecoratorCategory = pointer_decorator_category >
class decorator_chain
{
public:

    typedef Class class_type;
    typedef DecoratorCategory category_type;

    decorator_chain( bool owner_of_class = false , bool owner_of_decorators = false )
    {
        // ...
    }

    ~decorator_chain( void )
    {
        // ...
    }

    template< class Factory >
    void create_class( Factory make_class = Factory() )
    {
        // ...
    }

    template< class Factory >
    void decorate( Factory make_class = Factory() )
    {
        // ...
    }

    class_type& get_class_reference( void ) { return *m_current; }
    const class_type& get_class_reference( void ) const { return *m_current; }

    class_type* get_class_pointer( void ) { return m_current; }
    const class_type* get_class_pointer( void ) const { return m_current; }
};

该类接受两个模板参数。 一个是应该被装饰的组件的类型。 这个类型可以是 abstract。 第二个参数指示应该被装饰的对象是作为指针还是作为引用传递给装饰器。 构造函数有两个参数,它们指定尾部元素是否由 decorator_chain 拥有,以及装饰器是否由 decorator_chain 拥有。 如果它们被拥有,则在调用 decorator_chain 的析构函数时,decorator_chain 将销毁这些对象,否则不会。 可以通过 get_class_referenceget_class_pointer 访问头部(当前)元素。

主要工作由 create_classdecorate 完成。 第一个方法创建链的尾部元素。 它假定 Factory 是一个函数对象,返回 class_type 的新实例。 此函数对象不接受任何参数。 一个简单的例子是

abstract_class* make_concrete_class( void )
{
    return new concrete_class;
}

第二个方法创建一个 decorator 对象。 在这里,Factory 是一个函数对象,它接受一个参数 - 应该被装饰的组件。 这样一个函数的简单例子是

abstract_class* make_decorator( abstract_class *ac )
{
    return new decorator( ac );
}

您可能想知道这种调用 new 的奇怪方式,但这里 boost::factory 模板发挥了作用。 它封装了对 new 的调用,因此上面的示例可以写成

int main( int argc , char **argv )
{
    decorator_chain< abstract_class > db;
    db.create_class( boost::factory< concrete_class* >() );
    db.decorate( boost::bind( boost::factory< decorator* >() , _1 ) );
    db.decorate( boost::bind( boost::factory< decorator* >() , _1 ) );
    db.decorate( boost::bind( boost::factory< decorator* >() , _1 ) );
    db.get_class_reference().do_something();

    return 0;
}

请注意,借助 bind 和占位符 _1,您可以多么轻松地构建一个函数(将装饰状态作为参数)。

您还可以使用 boost::bind 进行参数规范化,因此可以将值传递给构造函数。 boost::ref 可用于传递引用

class int_decorator : public abstract_class
{
    abstract_class *m_decorated_class;
    int m_val;
public:
    int_decorator( abstract_class *decorated_class , int val ) : 
	m_decorated_class( decorated_class ) , m_val( val ) { }
    void do_something( void )
    {
        m_decorated_class->do_something();
        cout << "int_decorator : " << m_val << "\n";
    }
};

class string_decorator : public abstract_class
{
    abstract_class *m_decorated_class;
    string &m_val;
public:
    string_decorator( abstract_class *decorated_class , string &val ) : 
		m_decorated_class( decorated_class ) , m_val( val ) { }
    void do_something( void )
    {
        m_decorated_class->do_something();
        cout << "string_decorator " << m_val << "\n";
    }
};

int main( int argc , char **argv )
{
    string str = "Hello world!";
    decorator_chain< abstract_class > dc;
    dc.create_class( boost::factory< concrete_class* >() );
    dc.decorate( boost::bind( boost::factory< decorator* >() , _1 ) );
    dc.decorate( boost::bind( boost::factory< int_decorator* >() , _1 , 10 ) );
    dc.decorate( boost::bind( boost::factory< string_decorator* >() , 
		_1 , boost::ref( str ) ) );
    dc.get_class_reference().do_something();

    return 0;
}

如果必须使用对装饰对象的引用来调用装饰器的构造函数,则可以将 decorator_chain 的第二个模板参数设置为 reference_decorator_category

结论

在本文中,我已经展示了一种构建复杂的装饰器链的简单方法。 它大量使用了 boost::factoryboost::bind。 可能的扩展包括

  • 动态装饰器链。 这要求可以单独设置装饰器,类似于 decorator.set_decorated_state(decorated_state)
  • 链可以实现 abstract 类型,使其表现得像组件一样。

如果您有一些有趣的观点、批评或建议,请告诉我。

如何编译?

在 Linux 下编译示例很简单

g++ -I/path/to/boost example1.cpp -o example1
# or
g++ -I/path/to/boost example2.cpp -o example2

其中 /path/to/boost 指向 boost 库的目录。 您至少需要 1.43 版本。

参考文献

历史

  • 2010.10.20 初始版本
© . All rights reserved.