2016-06-20 19 views
2

この時点では、シグナルを放出するタイミングと別のクラスのメソッドを直接呼び出す(同じスレッド)というジレンマがあります。たとえば、チュートリアルでは、インストゥルメントクラス(モデル)のNotifyConnected信号を 'this'というビューマネージャのonConnectedスロットに接続しています。SetupViewManager :: WireButtons()、コードの3行目を参照してください。 (MVVMデザインパターンを使用しています)。インストゥルメントクラス(モデル)がView Managerについて何も知らないようにすると、信号とスロットが意味を持ちます。 (つまり、ビューマネージャの参照をモデルに渡すことは、MVVMのデザインパターンを破るため、いいえです)。コール関数は直接シグナルを発信します。(Qt - シグナルとスロット)

私が持っている問題は、次にViewManagerのonConnectedスロットが他の信号を出してから別のViewクラス、つまりSetupTabのスロットに手作業で接続する必要があることです。(ref void SetupViewManager :: onConnectedコード内のSetupViewManager :: WireDisplayUpdate()を無効にします)。

私の質問は、onConnectedスロットのすべての放射をSetupTabのメソッドを直接呼び出すと置き換えてみませんか?私にコードを複雑にするような気分です。

信号を放射するために余分なスペースを使い、リファレンスを持っている別のクラスのパブリック関数(信号)を単に呼び出すだけで何かを結ぶ必要があるのは何ですか?マルチスレッドアプリケーションではありません(信号とスロットはスレッドセーフです)。

私に教えてください。

ありがとうございました。

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); 
    } 
} 
+0

あなたのテキストフォーマットを変更することができますので、目を読むのが簡単ですか?また、あるコードがその質問をもう少し明確にするかもしれません。 –

+0

ビューを 'ViewManger'に設定していますか?これらの接続をしてはいけませんか?私たちがこれらのクラスについて何も知らない時を伝えることは非常に難しいです。 – thuga

+0

要求が完了しました。親切にコードを参照してください。ありがとうございました! – Nokiaowner

答えて

1

信号とスロットは、クラスを分離するために使用されます。そのため、誰がその機能をどのように使用するのかを明示的に知る必要はありません。多くの場合、デカップリングはソフトウェア設計の望ましい特性です。もちろん、それだけでは終わりではありません。コードの正当性を推論するのに役立ち、メンテナンスを容易にするのに役立ちます。コードの理解と推論の助けを借りて、それを分離して分析できる小さなコード単位に導きます。

クラスのペアがあり、それらを結合するかどうかを決定したい場合は、それらを他のクラスで使用できるかどうかを考えてください。 ABに結合できますが、ペアを結合するインターフェイスはBではなくCで使用できますか?そうであれば、いくつかのデカップリング・パターンを使用しなければならず、信号スロット・パターンもその1つです。

たとえば、これらの2つのインターフェイスがユーザーコードとの結合にどのように影響するかを比較しましょう。目的は単純です:オブジェクトのデストラクタにデバッグ出力を追加します。

class QObject { 
    ... 
    Q_SIGNAL void destroyed(QObject * obj = Q_NULLPTR); 
}; 

class QObjectB { 
    ... 
    virtual void on_destroyed(); 
}; 

int main() { 
    QObject a; 
    struct ObjectB : QObjectB { 
    void on_destroyed() override { qDebug() << "~QObjectB"; } 
    } b; 
    QObject::connect(&a, &QObject::on_destroyed, []{ qDebug() << "~QObject"; }); 
} 

信号スロットのインターフェイスは、あなたが簡単にそれらをサブクラス化することなく、既存のオブジェクトに機能を追加することができます。 Observerパターンの特に柔軟な実装です。これにより、コードとオブジェクトのコードが分離されます。

テンプレートメソッドのlookalikeパターンを使用する2番目の実装では、より密接なカップリングが必要です。ObjectBの破壊を処理するには、目的の機能を実装する派生クラスのインスタンスが必要です。

+0

すばらしい答え。乾杯:) – Nokiaowner

2

利点:

  • あなたのクラスは、それがクライアントですに関する情報を持っていないときに使いやすいです。
  • は、スレッドセーフな呼び出しに使用できます。
  • 通知するすべてのオブジェクトを手動で覚えておく必要はありません。
  • 2つのオブジェクトを接続する唯一のルールは、両方ともQObjectサブクラスでなければならないということです。

短所:

  • 遅いコール(各信号が接続されているすべてのオブジェクトのスキャンリストを発します)。
  • 複雑なスパゲッティコード。誰にいつスロットを呼び出すか、誰が発射信号を発するかは分かりません。

あなたはあなたの事についてあなた自身を考えるべきです。 SetupViewManagerの外部に「リスナー」という信号がない場合は、ダイレクトコールを試みます。他の誰かがこの信号に接続できる場合、あなたが選択したものがそれらの信号を放射しています。

信号を使用する他の理由もあります。しかし、単に関数を呼び出すためにそれらを使用する理由はありません。 1つのスレッドで、少なくとも。

関連する問題