2007 年 8 月 25 日 23 時 59 分

ホストの作成 #1: コードの機能分割


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


ホストでは、C++/CLI を使って開発をすることになるが、
既存の C++ コードと混ざる形となるため、
プログラムの見通しが悪くなりやすい。

そこで、ホストのコードを機能に合わせて分割しよう。
C++ ピュアな部分と C++/CLI の部分にだ。
そうしておけば、C++/CLI の導入を最低限で済ませられる。

1. scrnsave.lib 用の関数実装 (module.cpp, C++)
2. Application クラス(Application.cpp, C++)
3. Dispatcher クラス(Dispatcher.cpp, C++/CLI)

module.cpp には、scrnsave.lib を使うために必要な、
コールバック関数 3 つを含めて実装する。
つまり、ライブラリ仕様を満たすためだけの存在とする。

オブジェクト指向開発においては、
なるべくグローバル空間に名前を置きたくないため、
これら関数は、Application クラスのメソッドを呼ぶだけの、
単なるラッパー関数という感じである。

Application.cpp は、module.cpp から呼び出され、
scrnsave.lib のコールバックを処理するコードを記述する。
プラグインを呼び出す等、C++/CLI が必要な処理は、
このクラスに書かず、Dispatcher クラスに置く。

Dispatcher.cpp には、プラグインを呼び出す処理を置く。
C++/CLI が必要なコードはここに集約することになる。

例によって、普通はここまで分ける必要はないんだろうが、
単に、静的メソッドやグローバルを最大限排除し、
名前空間を導入するのが好きなだけだ。

では、最初に Application クラスの仕様を決める。

========== Application.hpp ==========

#ifndef z_loafer_screensaver_host_application
#define z_loafer_screensaver_host_application

#include <windows.h>
#include "Dispatcher.hpp"

namespace Loafer {
namespace ScreenSaver {
namespace Host {

class Application {
public:
    Application();
    virtual ~Application();

    virtual BOOL RegisterClasses(HANDLE hInst);

    virtual BOOL ConfigureProc(
            HWND hdlg, UINT message,
            WPARAM wParam, LPARAM lParam);

    virtual LRESULT MainProc(
        HWND hwnd, UINT message,
        WPARAM wParam, LPARAM lParam);

protected:
    Dispatcher dispatcher;

private:
    Application(const Application &);
    Application &operator =(const Application &);
};

}}} // Loafer::ScreenSaver::Host

#endif // !z_loafer_screensaver_host_application

========== end of Application.hpp ==========

何のことはない。scrnsave.lib の規約を、
単にクラスのメソッドに置き換えただけだ。
ここではとりあえずクラスにしておく。

こだわるなら、dispatcher メンバを削除し、
IApplication 等のインタフェースにするんだろうが、
そこまですると、もはやオブジェクト指向症候群だ。

次に、Dispatcher クラスの仕様を決める。
このクラスの内部では C++/CLI を使うことになるが、
外部に見せる仕様は C++ だけで書くようにしておく。
でないと、呼び出し側にも C++/CLI が必要になってしまう。

========== Dispatcher.hpp ==========

#ifndef z_loafer_screensaver_host_dispatcher
#define z_loafer_screensaver_host_dispatcher

#include <windows.h>

namespace Loafer {
namespace ScreenSaver {
namespace Host {

class Dispatcher {
public:
    Dispatcher();
    virtual ~Dispatcher();
    virtual void Configure(HWND hdlg);
    virtual void Create(HWND hwnd);
    virtual void Destroy(HWND hwnd);
    virtual void Paint(HWND hwnd);
private:
    Dispatcher(const Dispatcher &);
    Dispatcher &operator =(const Dispatcher &);
};

}}} // Loafer::ScreenSaver::Host

#endif // !z_loafer_screensaver_host_dispatcher

========== end of Dispatcher.hpp ==========

こりゃ解りやすい分け方になった。
簡単にいえば、ただのメッセージハンドラだ。

Application クラスのメソッドは、
ウィンドウプロシージャなので、
そこで拾った一部のメッセージを、
Dispatcher のメソッドに振り分ける感じだ。

メソッドの分け方は、ISaver インタフェースに似せてある。
つまり、Dispatcher クラスが、
ウィンドウプロシージャ仕様(Windows API)と、
ISaver 仕様の間を変換する Adapter となるわけだ。

これで、ホストの実装は分割され、
module.cpp, Application.cpp, Dispatcher.cpp の
3 つのソースファイルにて定義されることとなる。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.