Helper辅助类设计技巧

10 篇文章 0 订阅

Helper辅助类设计技巧

需求

当点击一个按钮后,执行功能代码。按钮文字为“执行”,代码执行成功后按钮闪烁显示“成功”,失败闪烁显示“失败”。闪烁完成后还原显示“执行”。

注:平台为Qt。

基础版

按照常规思路,点击按钮后,设置按钮文字,启动一个定时器。在定时器中刷新按钮字体颜色,记录定时器执行次数,完成后还原按钮文字。废话少说,上代码:

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0)
        :QMainWindow(parent)
    {
        ui.setupUi(this);
    }
    ~MainWindow(){}

private slots:
    void on_pushButton_clicked()
    {
        bool success = true;
        ui.pushButton->setText(success ? "成功" : "失败");
        m_times = 10;

        flash();
    }
    void flash()
    {
        static bool b = true;

        b = !b;
        m_times--;
        if (m_times > 0)
        {
            ui.pushButton->setStyleSheet(b ? "color:red;" : "color:blue;");
            QTimer::singleShot(100, this, SLOT(flash()));
        }
        else
        {
            ui.pushButton->setText("执行");
            ui.pushButton->setStyleSheet( "");
        }
    }

private:
    Ui::MainWindowClass ui;
    int m_times;
};

进阶版

上面的代码能够很好地工作,但是却存在几个问题:

  • 如果另外一个按钮也需要同样的功能,我们必须再次写同样的代码,代码量线性增加。
  • 如果这些代码有bug,修复bug的代码是成线性关系的。

针对这个问题,我们第一直觉当然是实现一个可以有闪烁功能的按钮类:

class FlashButton :public QPushButton
{
    Q_OBJECT
public:
    FlashButton(QWidget* parent = nullptr)
        :QPushButton(parent){}

public slots:
    void flash(const QString& text)
    {
        m_times = 10;
        m_srcText = this->text();
        flash();
    }

private slots:
    void flash()
    {
        static bool b = true;

        b = !b;
        m_times--;
        if (m_times > 0)
        {
            this->setStyleSheet(b ? "color:red;" : "color:blue;");
            QTimer::singleShot(100, this, SLOT(flash()));
        }
        else
        {
            this->setStyleSheet("");
            setText(m_srcText);
        }
    }

private:
    int m_times;
    QString m_srcText;
};

然后之前的代码就像这样:

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0)
        :QMainWindow(parent)
    {
        ui.setupUi(this);

        m_button = new FlashButton(this);
        m_button->setText("执行");
        ui.verticalLayout->addWidget(m_button);
    }
    ~MainWindow(){}

private slots:
    void on_pushButton_clicked()
    {
        bool success = true;
        m_button->flash(success ? "成功" : "失败");
    }

private:
    Ui::MainWindowClass ui;
    FlashButton* m_button;
};

相比基础版,主程序的代码少了许多,并且逻辑也更清晰。最重要的是解决了基础版存在的问题。

Helper版

但是,进阶版的代码同样存在一些问题:

  • 我们只能在代码中创建我们的按钮,没法在Qt Designer中进行布局,这是一件很不爽的事情。
  • 对于现有的代码,我们想增加此功能的代码也很大,尤其是需要修改的按钮如果也是一个QPushButton的子类的时候。

那如果很好地解决这些问题呢?叮咚,Helper版闪亮登场!

class FlashButtonHelper : public QObject
{
    Q_OBJECT
public:
    FlashButtonHelper(QPushButton* button)
        :QObject(button), m_button(button){}

public slots :
    void flash(const QString& text)
    {
        m_times = 10;
        m_srcText = m_button->text();
        flash();
    }

private slots:
    void flash()
    {
        static bool b = true;

        b = !b;
        m_times--;
        if (m_times > 0)
        {
            m_button->setStyleSheet(b ? "color:red;" : "color:blue;");
            QTimer::singleShot(100, this, SLOT(flash()));
        }
        else
        {
            m_button->setStyleSheet("");
            m_button->setText(m_srcText);
        }
    }

private:
    QPushButton* m_button;
    int m_times;
    QString m_srcText;
};
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0)
        :QMainWindow(parent)
    {
        ui.setupUi(this);
    }
    ~MainWindow(){}

private slots:
    void on_pushButton_clicked()
    {
        bool success = true;

        FlashButtonHelper* flasher = new  FlashButtonHelper(ui.pushButton);
        flasher->flash(success ? "成功" : "失败");
    }

private:
    Ui::MainWindowClass ui;
};

这样我们又可以在Qt Designer中愉快地进行界面布局了,并且如果需要对现有代码增加功能,我们需要做的工作是否简单,在需要的地方添加如下代码即可:

FlashButtonHelper* flasher = new  FlashButtonHelper(button);
flasher->flash("成功");

优化改进

对于大项目,很多时候需要对代码精雕细琢。在上面的代码中,如果我们点击按钮100次,那么就会有100个Helper类创建,这并不是我们想要的。因为其实只需要一个Helper即可(当然可以在适当的时候销毁,但是我们并不想在主程序中增加代码)。单例类为我们提供了解决思路,创建是首先判断对象是否存在,存在则返回存在的的对象即可:


class FlashButtonHelper : public QObject{
    Q_OBJECT
public:
    ~FlashButtonHelper(){ instances()->remove(m_button); }
    static FlashButtonHelper* getInstance(QPushButton* obj){
        if (!instances()->contains(obj)){
            (*instances())[obj] = new FlashButtonHelper(obj);
        }
        return (*instances())[obj];
    }

public slots :
    void flash(const QString& text)
    {
        m_times = 10;
        m_srcText = m_button->text();
        flash();
    }

private slots:
    void flash()
    {
        static bool b = true;

        b = !b;
        m_times--;
        if (m_times > 0)
        {
            m_button->setStyleSheet(b ? "color:red;" : "color:blue;");
            QTimer::singleShot(100, this, SLOT(flash()));
        }
        else
        {
            m_button->setStyleSheet("");
            m_button->setText(m_srcText);
        }
    }

private:
    FlashButtonHelper(QPushButton* button)
        : QObject(button), m_button(button){}

    static QHash<QPushButton*, FlashButtonHelper*>* instances(){
        static QHash<QPushButton*, FlashButtonHelper*> s_instances;
        return &s_instances;
    }
private:
    QPushButton* m_button;
    int m_times;
    QString m_srcText;
};

#define FLASH_HELPER(btn)  FlashButtonHelper::getInstance(btn)
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0)
        :QMainWindow(parent)
    {
        ui.setupUi(this);
    }
    ~MainWindow(){}

private slots:
    void on_pushButton_clicked()
    {
        bool success = true;
        FLASH_HELPER(ui.pushButton)->flash(success ? "成功" : "失败");
    }

private:
    Ui::MainWindowClass ui;
};

关键点:

  • 采用静态hash表保存所有的Helper对象。
  • Helper构造函数将自己加入hash表,析构时从hash表中移除。
  • Helper构造函数私有化,由getInstance执行是否创建Helper对象。
  • Helper采用Qt的QObject进行内存管理。在目标对象销毁时自动销毁自己。
  • 定义了FLASH_HELPER让代码更易读。

结语:上面的FlashHelper基本上就是一个Helper类的标准模板。俺就是抛砖一下,相信大家可以举一反三,设计更多的属于自己的优雅的Helper了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值