设计模式(5)-装饰模式(Decorator)

来源:岁月联盟 编辑:exp 时间:2012-07-10

【描述】不修改原代码的结构,通过装饰器给代码增加新功能。

【UML图】

 /

图1 UML图

(1) 原始代码为Component类,提供了operation操作;

(2) 装饰器为Decorator类,提供了扩展的operation功能;

(3) 注意与模板模式(设计模式(1)-模板模式(Template))的区别。

 

【示例代码】

component.h

[html]
#ifndef COMPONENT_H 
#define COMPONENT_H 
 
class Component 

public: 
    Component(); 
 
public: 
    virtual void operation(); 
}; 
 
#endif // COMPONENT_H 
#ifndef COMPONENT_H
#define COMPONENT_H

class Component
{
public:
    Component();

public:
    virtual void operation();
};

#endif // COMPONENT_H
 

component.cpp

[html] view plaincopyprint?#include <QDebug> 
#include "component.h" 
 
Component::Component() 

    qDebug()<<"construct Component"; 

 
void Component::operation() 

    qDebug()<<"Base Function"; 

#include <QDebug>
#include "component.h"

Component::Component()
{
    qDebug()<<"construct Component";
}

void Component::operation()
{
    qDebug()<<"Base Function";
}
 

decorator.h

[html]
#ifndef DECORATOR_H 
#define DECORATOR_H 
 
#include "component.h" 
 
class Decorator : public Component 

public: 
    Decorator(Component component); 
 
private: 
    Component component; 
 
public: 
    void operation(); 
}; 
 
#endif // DECORATOR_H 
#ifndef DECORATOR_H
#define DECORATOR_H

#include "component.h"

class Decorator : public Component
{
public:
    Decorator(Component component);

private:
    Component component;

public:
    void operation();
};

#endif // DECORATOR_H
 

decorator.cpp

[html]
#include <QDebug> 
#include "decorator.h" 
 
Decorator::Decorator(Component component) 

    qDebug()<<"construct Decorator"; 
    this->component = component; 

 
void Decorator::operation() 

    component.operation(); 
    qDebug()<<"Extend Function"; 

#include <QDebug>
#include "decorator.h"

Decorator::Decorator(Component component)
{
    qDebug()<<"construct Decorator";
    this->component = component;
}

void Decorator::operation()
{
    component.operation();
    qDebug()<<"Extend Function";
}
 

main.cpp

[html]
#include "component.h" 
#include "decorator.h" 
 
int main(void) 

    Component component; 
    component.operation(); 
    Decorator decorator(component); 
    decorator.operation(); 
 
    return 0; 

#include "component.h"
#include "decorator.h"

int main(void)
{
    Component component;
    component.operation();
    Decorator decorator(component);
    decorator.operation();

    return 0;
}
 

【运行结果】

[html]
construct Component  
Base Function  
construct Component  
construct Component  
construct Decorator  
Base Function  
Extend Function  
construct Component
Base Function
construct Component
construct Component
construct Decorator
Base Function
Extend Function 

【结果分析】

借助装饰器,在没有改变原始代码的前提下,给代码增加了新功能。

 

【实例剖析】

设计模式(1)-模板模式(Template)一文介绍了一种改进的Qt嵌入式输入法,下面利用装饰模式对代码进行改写。先看UML图:

 

图2

(1) 原始代码为QLineEdit类;

(2) 装饰器为QLineEditWithIM类,提供了installIM()方法。

 

【代码清单】

下面仅贴出修改的部分,详细代码请参考Qt输入法设计(嵌入式)以及设计模式(1)-模板模式(Template)一文。

qlineeditwithim.h

[html]
#ifndef QLINEEDITWITHIM_H 
#define QLINEEDITWITHIM_H 
 
#include <QLineEdit> 
#include "inputmethod.h" 
 
class QLineEditWithIM : public QLineEdit 

public: 
    QLineEditWithIM(QLineEdit *lineEdit); 
    ~QLineEditWithIM(); 
 
private: 
    QLineEdit *lineEdit; 
    InputMethod *im; 
 
public: 
    void installIM(); 
}; 
 
#endif // QLINEEDITWITHIM_H 
#ifndef QLINEEDITWITHIM_H
#define QLINEEDITWITHIM_H

#include <QLineEdit>
#include "inputmethod.h"

class QLineEditWithIM : public QLineEdit
{
public:
    QLineEditWithIM(QLineEdit *lineEdit);
    ~QLineEditWithIM();

private:
    QLineEdit *lineEdit;
    InputMethod *im;

public:
    void installIM();
};

#endif // QLINEEDITWITHIM_H
 

qlineeditwithim.cpp

[html]
#include "qlineeditwithim.h" 
 
QLineEditWithIM::QLineEditWithIM(QLineEdit *lineEdit) 

//#ifdef Q_WS_QWS 
    im = new InputMethod; 
    this->lineEdit = lineEdit; 
//#endif 

 
QLineEditWithIM::~QLineEditWithIM() 

    delete im; 

 
void QLineEditWithIM::installIM() 

//#ifdef Q_WS_QWS 
    installEventFilter(im); 
    connect(im->keyboard,SIGNAL(setvalue(QString)),this,SLOT(setText(QString))); 
//#endif 

#include "qlineeditwithim.h"

QLineEditWithIM::QLineEditWithIM(QLineEdit *lineEdit)
{
//#ifdef Q_WS_QWS
    im = new InputMethod;
    this->lineEdit = lineEdit;
//#endif
}

QLineEditWithIM::~QLineEditWithIM()
{
    delete im;
}

void QLineEditWithIM::installIM()
{
//#ifdef Q_WS_QWS
    installEventFilter(im);
    connect(im->keyboard,SIGNAL(setvalue(QString)),this,SLOT(setText(QString)));
//#endif
}
 

login.h

[html]
#ifndef LOGIN_H 
#define LOGIN_H 
 
#include <QDialog> 
#include "qlineeditwithim.h" 
 
class QLabel; 
class QLineEdit; 
class QDialogButtonBox; 
 
class QLogin : public QDialog 

    Q_OBJECT 
 
public: 
    QLogin(); 
    ~QLogin(); 
 
public: 
 
    QLabel *managerLabel; 
    QLabel *passwdLabel; 
 
    QLineEditWithIM *managerEdit; 
    QLineEditWithIM *passwdEdit; 
 
    QPushButton *okButton; 
    QPushButton *cancelButton; 
    QDialogButtonBox *buttonBox; 
 
signals: 
    void Authorize(); 
 
private slots: 
    void login(); 
    void cancel(); 
 
}; 
 
#endif // LOGIN_H 
#ifndef LOGIN_H
#define LOGIN_H

#include <QDialog>
#include "qlineeditwithim.h"

class QLabel;
class QLineEdit;
class QDialogButtonBox;

class QLogin : public QDialog
{
    Q_OBJECT

public:
    QLogin();
    ~QLogin();

public:

    QLabel *managerLabel;
    QLabel *passwdLabel;

    QLineEditWithIM *managerEdit;
    QLineEditWithIM *passwdEdit;

    QPushButton *okButton;
    QPushButton *cancelButton;
    QDialogButtonBox *buttonBox;

signals:
    void Authorize();

private slots:
    void login();
    void cancel();

};

#endif // LOGIN_H
 

login.cpp

[html]
#include <QtGui> 
#include "login.h" 
 
QLogin::QLogin() 

    managerLabel = new QLabel(tr("&Manager:")); 
    QLineEdit *_managerEdit = new QLineEdit; 
    managerEdit = new QLineEditWithIM(_managerEdit); 
    managerEdit->installIM(); 
    managerLabel->setBuddy(managerEdit); 
 
    passwdLabel = new QLabel(tr("&Passwd:")); 
    QLineEdit *_passwdEdit = new QLineEdit; 
    passwdEdit = new QLineEditWithIM(_passwdEdit); 
    passwdEdit->installIM(); 
    passwdEdit->setEchoMode(QLineEdit::Password); 
    passwdLabel->setBuddy(passwdEdit); 
 
    okButton = new QPushButton(tr("&Login")); 
    cancelButton = new QPushButton("&Cancel"); 
 
    okButton->setDefault(true); 
 
    buttonBox = new QDialogButtonBox; 
    buttonBox->addButton(okButton, QDialogButtonBox::ActionRole); 
    buttonBox->addButton(cancelButton, QDialogButtonBox::AcceptRole); 
 
    connect(okButton, SIGNAL(clicked()), this, SLOT(login())); 
    connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancel())); 
 
    QHBoxLayout *topLayout = new QHBoxLayout; 
    topLayout->addWidget(managerLabel); 
    topLayout->addWidget(managerEdit); 
 
    QHBoxLayout *midLayout = new QHBoxLayout; 
    midLayout->addWidget(passwdLabel); 
    midLayout->addWidget(passwdEdit); 
 
    QVBoxLayout *mainLayout = new QVBoxLayout; 
    mainLayout->addLayout(topLayout); 
    mainLayout->addLayout(midLayout); 
    mainLayout->addWidget(buttonBox); 
    mainLayout->setMargin(20); 
    setLayout(mainLayout); 
    managerEdit->setFocus();   
 
    QIcon icon; 
    icon.addFile(QString::fromUtf8(":/new/main/picture/logo.png"), QSize(), QIcon::Normal, QIcon::Off); 
    setWindowIcon(icon); 
    setWindowTitle("Login"); 

 
QLogin::~QLogin() 

    qDebug()<<"destruct login"; 
    delete managerLabel; 
    delete managerEdit; 
    delete passwdLabel; 
    delete passwdEdit; 
    delete okButton; 
    delete cancelButton; 

 
/* 
* Name : void login() 
* Type : slot 
* Func : login when authorize 
* In   : Null 
* Out  : Null 
*/ 
void QLogin::login() 

    qDebug()<<managerEdit->text(); 
    qDebug()<<passwdEdit->text(); 

 
 
/* 
* Name : void cancel() 
* Type : slot 
* Func : cancel login 
* In   : Null 
* Out  : Null 
*/ 
void QLogin::cancel() 

    managerEdit->clear(); 
    passwdEdit->clear(); 
    close(); 

#include <QtGui>
#include "login.h"

QLogin::QLogin()
{
    managerLabel = new QLabel(tr("&Manager:"));
    QLineEdit *_managerEdit = new QLineEdit;
    managerEdit = new QLineEditWithIM(_managerEdit);
    managerEdit->installIM();
    managerLabel->setBuddy(managerEdit);

    passwdLabel = new QLabel(tr("&Passwd:"));
    QLineEdit *_passwdEdit = new QLineEdit;
    passwdEdit = new QLineEditWithIM(_passwdEdit);
    passwdEdit->installIM();
    passwdEdit->setEchoMode(QLineEdit::Password);
    passwdLabel->setBuddy(passwdEdit);

    okButton = new QPushButton(tr("&Login"));
    cancelButton = new QPushButton("&Cancel");

    okButton->setDefault(true);

    buttonBox = new QDialogButtonBox;
    buttonBox->addButton(okButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(cancelButton, QDialogButtonBox::AcceptRole);

    connect(okButton, SIGNAL(clicked()), this, SLOT(login()));
    connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancel()));

    QHBoxLayout *topLayout = new QHBoxLayout;
    topLayout->addWidget(managerLabel);
    topLayout->addWidget(managerEdit);

    QHBoxLayout *midLayout = new QHBoxLayout;
    midLayout->addWidget(passwdLabel);
    midLayout->addWidget(passwdEdit);

    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addLayout(topLayout);
    mainLayout->addLayout(midLayout);
    mainLayout->addWidget(buttonBox);
    mainLayout->setMargin(20);
    setLayout(mainLayout);
    managerEdit->setFocus(); 

    QIcon icon;
    icon.addFile(QString::fromUtf8(":/new/main/picture/logo.png"), QSize(), QIcon::Normal, QIcon::Off);
    setWindowIcon(icon);
    setWindowTitle("Login");
}

QLogin::~QLogin()
{
    qDebug()<<"destruct login";
    delete managerLabel;
    delete managerEdit;
    delete passwdLabel;
    delete passwdEdit;
    delete okButton;
    delete cancelButton;
}

/*
* Name : void login()
* Type : slot
* Func : login when authorize
* In   : Null
* Out  : Null
*/
void QLogin::login()
{
    qDebug()<<managerEdit->text();
    qDebug()<<passwdEdit->text();
}


/*
* Name : void cancel()
* Type : slot
* Func : cancel login
* In   : Null
* Out  : Null
*/
void QLogin::cancel()
{
    managerEdit->clear();
    passwdEdit->clear();
    close();
}

 

【分析】

(1) 与模板模式的关键区别在于,QLineEditWithIM引用了QLineEdit对象;

(2) 模板模式调用的代码为

[html]
QLineEditWithIM *managerEdit;   
managerEdit = new QLineEditWithIM; 
QLineEditWithIM *managerEdit; 
managerEdit = new QLineEditWithIM;
装饰模式调用的代码为

[html] 
QLineEdit *_managerEdit = new QLineEdit; 
managerEdit = new QLineEditWithIM(_managerEdit); 
managerEdit->installIM(); 
QLineEdit *_managerEdit = new QLineEdit;
managerEdit = new QLineEditWithIM(_managerEdit);
managerEdit->installIM();
实质上,在模板模式中,我们并没有使用QLineEditWithIM提供的引用对象lineEdit。而运用模板模式调用也比较简单。可见,此处将QLineEdit想象为一个模板,运用模板模式更为合适。

 作者:tandesir