在这一点上,我处于一个两难境地,即何时发出信号与直接调用另一个类中的方法(相同的线程) . 例如,在我正在做的教程中,我正在将Instrument类(Model)的NotifyConnected信号连接到'this'又名View Manager的onConnected插槽,请参阅SetupViewManager :: WireButtons(),代码中的第三行 . (我正在使用MVVM设计模式) . 这里的信号和插槽很有意义,因为Instruments类(Model)不应该对View Manager有任何了解 . (即将视图管理器的引用传递给模型是否定的,因为它会打破MVVM设计模式 . )很棒 .
我的问题是,在教程中接下来,ViewManager的onConnected插槽然后发出其他信号,然后我必须继续手动连接到另一个View类的插槽,即SetupTab(ref void SetupViewManager :: onConnected和void SetupViewManager ::代码中的WireDisplayUpdate()) .
我的问题是,为什么不直接调用SetupTab中的方法替换onConnected插槽中的所有发出?感觉像是过于复杂的代码 .
为了简单地从我引用的另一个类中调用一个公共函数(信号),为了发出信号而不得不连接所有内容,有什么好处呢?它不是一个多线程应用程序(我知道信号和插槽是线程安全的) .
请赐教 .
谢谢 .
setupviewmanager.cpp:
#include "setupviewmanager.h"
#include "View/setuptab.h"
#include "Model/instrument.h"
#include "Model/settings.h"
#include "utils.h"
namespace Ps
{
SetupViewManager::SetupViewManager(QObject *parent,
SetupTab &tab,
Instrument &inst,
Settings &config) :
QObject(parent),
m_setupTab(tab),
m_instrument(inst)
{
WireSettings(config);
config.ParseJsonData();
WireHostAndPort();
WireMessages();
WireButtons();
WireDisplayUpdate();
m_setupTab.SetHostName(config.getHostName());
m_setupTab.SetPort(config.getPortNumber());
m_setupTab.SetCommands(config.getCommandsAsModel());
auto long_wait = config.getLongWaitMs();
auto short_wait = config.getShortWaitMs();
m_instrument.SetlongWaitMs(long_wait);
m_instrument.SetShortWaitMs(short_wait);
emit NotifyStatusUpdated(tr("Long wait Ms: %1").arg(long_wait));
emit NotifyStatusUpdated(tr("Short Wait Ms: %1").arg(short_wait));
onDisconnected();
}
SetupViewManager::~SetupViewManager()
{
Utils::DestructorMsg(this);
}
void SetupViewManager::WireSettings(Settings &config)
{
connect(&config, &Settings::NotifyStatusMessage, &m_setupTab, &SetupTab::onStatusUpdated);
}
void SetupViewManager::WireHostAndPort()
{
connect(&m_setupTab, &SetupTab::NotifyHostNameChanged, &m_instrument, &Instrument::onHostNameChanged);
connect(&m_setupTab, &SetupTab::NotifyPortChanged, &m_instrument, &Instrument::onPortChanged);
}
void SetupViewManager::WireMessages()
{
connect(&m_instrument, &Instrument::NotifyErrorDetected, &m_setupTab, &SetupTab::onStatusUpdated);
connect(&m_instrument, &Instrument::NotifyStatusUpdated, &m_setupTab, &SetupTab::onStatusUpdated);
connect(this, &SetupViewManager::NotifyStatusUpdated, &m_setupTab, &SetupTab::onStatusUpdated);
}
void SetupViewManager::WireButtons()
{
connect(&m_setupTab, &SetupTab::NotifyConnectClicked,&m_instrument, &Instrument::Connect);
connect(&m_instrument, &Instrument::NotifyConnected, &m_setupTab, &SetupTab::onConnected);
connect(&m_instrument, &Instrument::NotifyConnected, this, &SetupViewManager::onConnected);
connect(&m_setupTab, &SetupTab::NotifyDisconnectClicked,&m_instrument, &Instrument::Disconnect);
connect(&m_instrument, &Instrument::NotifyDisconnected, &m_setupTab,&SetupTab::onDisconnected);
connect(&m_instrument, &Instrument::NotifyDisconnected, this, &SetupViewManager::onDisconnected);
connect(&m_setupTab, &SetupTab::NotifySendClicked,&m_instrument, &Instrument::onSendRequest);
connect(&m_instrument, &Instrument::NotifyDataSent,&m_setupTab, &SetupTab::onDataSent);
connect(&m_setupTab, &SetupTab::NotifyReceiveClicked,&m_instrument, &Instrument::onReceiveRequest);
connect(&m_instrument, &Instrument::NotifyDataReceived,&m_setupTab, &SetupTab::onDataReceived);
}
void SetupViewManager::WireDisplayUpdate()
{
connect (this, &SetupViewManager::NotifyConnectEnabled, &m_setupTab, &SetupTab::onConnectEnabled);
connect (this, &SetupViewManager::NotifyDisconnectEnabled, &m_setupTab, &SetupTab::onDisconnectEnabled);
connect (this, &SetupViewManager::NotifyDirectCommandsEnabled, &m_setupTab, &SetupTab::onDirectCommandsEnabled);
connect (this, &SetupViewManager::NotifyControlTabEnabled, &m_setupTab, &SetupTab::onControlTabEnabled);
}
void SetupViewManager::onConnected()
{
emit NotifyConnectEnabled(false); // HERE. Why not just call method directly with m_setupTab.onConnectEnabled(false); etc...?
emit NotifyDisconnectEnabled(true);
emit NotifyDirectCommandsEnabled(true);
emit NotifyControlTabEnabled(true);
}
void SetupViewManager::onDisconnected()
{
emit NotifyConnectEnabled(true);
emit NotifyDisconnectEnabled(false);
emit NotifyDirectCommandsEnabled(false);
emit NotifyControlTabEnabled(false);
}
}
2 回答
信号槽机制的优点:
当你的 class 没有关于它的客户信息时,
易于使用;
可用于线程安全调用;
你不能手动记住所有通知它们的对象;
连接两个对象的唯一规则是它们都必须是QObject子类 .
缺点:
较慢的呼叫(每个信号发出所有连接对象的扫描列表);
可能复杂的意大利面条代码;你不知道,谁和什么时候会打电话给任何一个插槽或者谁会发出信号 .
你应该考虑一下自己的情况 . 如果SetupViewManager外没有信号“侦听器”,请尝试直接调用 . 如果其他人可以连接到此信号,您的选择就是发出它们 .
使用信号也可能有其他原因 . 但没有理由只使用它们来调用函数 . 在一个线程中,至少 .
信号和插槽用于解耦类,因此他们无需明确知道谁使用了他们的功能以及如何使用 . 在许多情况下,解耦是软件设计的理想特征 . 当然,它本身并不是目的,它可以帮助您推断代码的正确性并使其更易于维护 . 解耦有助于理解/推理代码,因为它会导致您可以单独分析的较小代码单元 . 另一种看待它的方法是关注点的分离:让一个代码单元做一件事,例如:将一个课程集中在功能的一个方面 .
当你有一对类并希望决定是否结合它们时,请考虑它们是否可以与其他类一起使用 .
A
可以耦合到B
,但耦合该对的接口是否可以被C
而不是B
使用?如果是这样,那么必须使用一些去耦模式,并且信号槽模式就是其中之一 .例如,让我们比较这两个接口如何影响与用户代码的耦合 . 目标很简单:将调试输出添加到对象的析构函数:
信号槽接口允许您轻松地向现有对象添加功能,而无需对其进行子类化 . 它是Observer pattern的一个特别灵活的实现 . 这将您的代码与对象的代码分离 .
第二个实现,使用模板方法相似的模式,强制更紧密的耦合:要对_321637的破坏采取行动,您必须具有实现所需功能的派生类的实例 .