c++ builder > Observerパターンのテスト実装


動作確認
C++ Builder XE4

背景

関連 http://qiita.com/7of9/items/358a46b38ebf93c5101a
にてPresentation Domain Separationを教えてもらった。

MVVMなどの1つとしてObserverパターンを使う例があった。
そのObserverパターンをC++ Builderで動くように実装してみた。

注意: 以下のコードは試験実装です。単一責任原則の無視や実装者の理解不足によるまずい実装例が含まれます。

UnitObserver

3つのクラスの混在。

UnitObserver.h
//---------------------------------------------------------------------------

#ifndef UnitObserverH
#define UnitObserverH
//---------------------------------------------------------------------------

#include <System.Classes.hpp>
#include <vcl.h>
#include <vector>

class CObserver;

//---------------------------------------------------------------------------
// Subjectクラス
//
class CSubject {
    std::vector < class CObserver * > views;
    int value;

public:
    void __fastcall attach(CObserver *obs);
    void __fastcall setVal(int val);
    int __fastcall getVal();
    void __fastcall notify();
};

//---------------------------------------------------------------------------
// Observerクラス
//
class CObserver {
    CSubject *model;
    int denom;

protected:
    CSubject *getSubject();
    int getDivisor();

public:
    virtual void __fastcall update() = 0;
    __fastcall CObserver(CSubject *mod, int div);

};

//---------------------------------------------------------------------------
// DivObserverクラス
//
class CDivObserver : public CObserver {
public:
    __fastcall  CDivObserver(CSubject *mod, int div) : CObserver(mod, div){}
    void __fastcall update();
};

#endif
UnitObserver.cpp
//---------------------------------------------------------------------------

#pragma hdrstop

#include "UnitObserver.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)

int __fastcall CSubject::getVal()
{
    return value;
}

void __fastcall CSubject::setVal(int val)
{
    value = val;
    notify();
}

void __fastcall CSubject::notify()
{
    for(int idx=0; idx < views.size(); idx++) {
        views[idx]->update();
    }
}

void __fastcall CSubject::attach(CObserver *obs)
{
    views.push_back(obs);
}

//---------------------------------------------------------------------------
__fastcall CObserver::CObserver(CSubject *mod, int div)
{
    model = mod;
    denom = div;
    model->attach(this);
}

CSubject *CObserver::getSubject()
{
    return model;
}

int CObserver::getDivisor()
{
    return denom;
}

//---------------------------------------------------------------------------

void __fastcall CDivObserver::update()
{
    int v = getSubject()->getVal();
    int d = getDivisor();

    String msg = String().sprintf(L"%d div %d = %d", v, d, v/d);
    OutputDebugString(msg.c_str());
}

Unit1

Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE で管理されるコンポーネント
    TButton *Button1;
    void __fastcall Button1Click(TObject *Sender);
private:    // ユーザー宣言
public:     // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include "UnitObserver.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    CSubject sbj;

    CDivObserver divObs1(&sbj, 3);
    CDivObserver divObs2(&sbj, 1);
    CDivObserver divObs3(&sbj, 4);
    sbj.setVal(20);
}
//---------------------------------------------------------------------------

動作の理解

  • CDivObserver divObs1(&sbj, 3);などでattach処理(オブザーバへの登録)が行われる
  • setVal()実行時にnotify(登録しているものへの通知)が実行される
  • notify()の中ではそれぞれの登録へのupdate()処理を行う。

C++ Builder向けの最適なコードではないが、だいたいの感じをつかんだ。

エディタだけで実装していくと自分のC++の理解不足を痛感した。