C による実装

C による実装


(実装ファイル:responder.tar

これは、C による実装例だ。C でオブジェクト指向?オブジェクト指向の概念を実装するのに、オブジェクト指向言語である必要はない。ただ、言語がサポートしてくれないから、必然的にソースコードの量は多くなってしまうけどね。逆に、自由度を楽しめる、とも言えるかもしれない。この実装例は、qshinya さんにいただきました。ありがとうございました。

ちょっとソースコードが長いので、要点だけかいつまんでいこう。完全な実装は、こちらを参考にしてくれ。どうやって実装しているかというと、チェインを構成するために、CResponder という構造体を使っている。この構造体が、各オブジェクトをチェインにつなぎ、順々にたぐってハンドラを呼び出すんだ。

CResponder の定義と、リクエストを処理するところを見てみよう。

CResponder 定義

typedef struct CResponder {

 /* chain上位のResponder */
 struct CResponder* mSuper;

 /* responderをもっているオブジェクト */
 void* mOwner;
 /* mOwnerでRES_IDを受ける為の関数ポインタ(必須) */
 funcProcessResponder mFuncProcessResponder;


 ResponderState mState;
 /* オブジェクトがdeactiveからactiveになった時に */
 /* 呼ばれる関数(optional) */
 funcActiveResponder mFuncActiveResponder;
 /* オブジェクトがactiveからdeactiveになった時に */
 /* 呼ばれる関数(optional) */
 funcDeactiveResponder mFuncDeactiveResponder;

 /* chain下位のResponderリスト */
 CResponderListHead mSubResponderListHead;
 /* リストの為のエントリ */
 CResponderListEntry mEntry;

} CResponder;


ProcessResponder()

/*
 * ProcessResponder
 *
 * responder-chain にRES_IDを流す
 */
void
ProcessResponder(
    CResponder* inRes, /* 対象となるresponder */
    RES_ID inResID, /* 流すRES_ID */
    void* inInfo) /* inResIDの追加情報 */
{

  int isNextProcessResponder;

  if(inRes == NULL) return; /* 何もしない */

  /* check */
  assert(inRes->mFuncProcessResponder != NULL);
  assert(inRes->mOwner != NULL);


  /* mOwnerのmFuncProcessResponderに処理を移して */
  /* RES_IDを処理する */
  isNextProcessResponder =
  inRes->mFuncProcessResponder(
    inRes->mOwner,inResID,inInfo);


  if(isNextProcessResponder != 0 && inRes->mSuper) {
    /* 上位のResponderに処理を流す */
    ProcessResponder(inRes->mSuper,inResID,inInfo);
  }

}

つまり、CResponder にリクエストをうけるためのハンドラ mFuncProcessResponder を関数ポインタとして登録して、ProcessResponder() でチェインリクエストが渡されたら、そのハンドラをリクエストとともに呼び出すんだ。

呼び出されたハンドラは、リクエストを受け付けるかどうか判定する。ためしに、Button の実装を見てみよう。

CButton 定義

typedef struct CButton_{

  const char* mName;

  CResponder mResponderEntry;

} CButton;

CButton_ProcessResponder()

/*
* CButton_ProcessResponder
*
* responder-chain を流れてきたRES_IDを処理する
* 戻値:上のResponderへ処理を流すかどうか。
* non 0:流す,0:流さない
*/
int
CButton_ProcessResponder(
    void* inButton, /* CButtonオブジェクト */
    RES_ID inResID, /* chainを流れてきたRES_ID */
    void* inInfo) /* RES_IDの追加情報 */
{

  /* 上のResponderへ処理を流す */
  int isProcessNextChain = 1;

  switch(inResID){
  case RES_ID_HELLO:
    isProcessNextChain =
      CButton_DoHello(inButton);break;
  default:
    /* ここで処理をしないので上のResponderへ処理を流す */
    isProcessNextChain = 1;
  }

  return isProcessNextChain;
}

こんな感じだ。非常にプリミティブな感じだけど、とても見通しはよい。Chain of Responsibility の仕様に合わせて、好きなように変更できるところも素敵。ただもちろん、他の言語と比べると、圧倒的にソースコードの量は多くなってしまうけどね。