首页 > 解决方案 > 观察者模式不起作用

问题描述

所以我遇到了这个问题,我一直在为我的 OOP 考试练习,并且我尝试构建一个观察者设计模式。不幸的是,似乎每次我初始化一个接口时,它都会以某种方式复制我的控制器,这意味着观察者列表的新副本。因此,我的界面没有被更新。

我的观察者:

#pragma once
#include <vector>
#include <algorithm>
class Observer
{
public:
    virtual void update() = 0;
    virtual ~Observer() {}
};

class Observable
{
private:
    std::vector<Observer*> observers;

public:

    virtual ~Observable() {}
    void addObserver(Observer *obs)
    {
        observers.push_back(obs);
    }
    void removeObserver(Observer *obs)
    {
        observers.erase(std::remove(observers.begin(), observers.end(), obs));
    }
    void notify()
    {
        for (auto obs : observers)
        {
            obs->update();
        }
    }
};

这是需要更新的界面:

class Practice : public QWidget, public Observer
{
    Q_OBJECT

public:
    Practice(Controller& ctrl, Teacher& t, QWidget *parent = Q_NULLPTR);

--------------
Practice::Practice(Controller& ctrl, Teacher& t, QWidget *parent) : ctrl{ ctrl }, t{ t }, QWidget(parent)
{
    ui.setupUi(this);
    QObject::connect(ui.studentList, &QListWidget::itemSelectionChanged, this, [this]() {this->listItemChanged(); });
    QObject::connect(ui.gradeButton, &QPushButton::clicked, this, &Practice::on_gradeButton_clicked);
    this->populateStudentsList();
    this->ctrl.addObserver(this);
}

我的主要:(我打开的窗户和“老师”一样多)

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Repository repo("Student.txt", "Teacher.txt");
    Controller ctrl(repo);
    std::vector<Teacher> temp = ctrl.getRepo().getTeachers();
    for (int i = 0; i < temp.size(); ++i)
    {
        Practice* p = new Practice{ctrl, temp[i]};
        p->setWindowTitle(QString::fromStdString(temp[i].getName()));
        p->show();
    }
    return a.exec();
}

另外,我的控制器继承自 Observable。

我对其进行了调试,得出的结论是,每次初始化“练习”时都会创建一个新控制器,但我不知道为什么。

谢谢你。

标签: c++observer-pattern

解决方案


我对其进行了调试,得出的结论是,每次初始化“练习”时都会创建一个新控制器,但我不知道为什么。

您没有显示您的类的完整声明Practice,但从您的构造函数定义中可以清楚地看出该类中存在一个ctrl成员。

如果该ctrl成员未声明为引用 ( Controller&),则当构造函数ctrl{ ctrl }在成员初始化列表中调用时,即使输入对象通过引用传递给构造函数,也会生成输入对象的副本。ctrlctrl

如果该Controller对象的寿命超过Practice对象(似乎是这种情况),则考虑使该Practice::ctrl成员成为输入Controller对象的引用/指针,而不是它的副本

class Practice : public QWidget, public Observer
{
    ...
private:
    Controller &ctrl;
    ...
public:
    Practice(Controller& ctrl, ...);
    ~Practice();
    ...
};

Practice::Practice(Controller& ctrl, ...) : ctrl{ ctrl }, ...
{
    ...
    this->ctrl.addObserver(this);
}

Practice::~Practice()
{
    ...
    this->ctrl.removeObserver(this);
}

或者:

class Practice : public QWidget, public Observer
{
    ...
private:
    Controller *ctrl;
    ...
public:
    Practice(Controller& ctrl, ...);
    ~Practice();
    ...
};

Practice::Practice(Controller& ctrl, ...) : ctrl{ &ctrl }, ...
{
    ...
    this->ctrl->addObserver(this);
}

Practice::~Practice()
{
    ...
    this->ctrl->removeObserver(this);
}

Controller或者,考虑将对象包装在astd::shared_ptr<Controller>中,然后将Practice::ctrl成员声明为std::shared_ptr<Controller>匹配。

class Practice : public QWidget, public Observer
{
    ...
private:
    std::shared_ptr<Controller> ctrl;
    ...
public:
    Practice(std::shared_ptr<Controller> ctrl, ...);
    ...
};

Practice::Practice(std::shared_ptr<Controller> ctrl, ...) : ctrl{ ctrl }, ...
{
    ...
    this->ctrl->addObserver(this);
}

Practice::~Practice()
{
    ...
    this->ctrl->removeObserver(this);
}

int main()
{
    std::shared_ptr<Controller> ctrl = std::make_shared<Controller>(repo);
    ...
    Practice* p = new Practice{ctrl, temp[i]};
    ...
}

无论哪种方式,多个Practice对象都将能够共享一个Controller对象。


推荐阅读