オブザーバーパターン¶
目次
前節のコードで存在しないファイルを指定するとエラーメッセージが2回表示される.それは次のようにして file_error
が2回呼ばれるためである.
static bool reader(FileContext *p){
MyFileContext *pFileCtx = (MyFilecontext *)p;
MyBufferContext *pBufCtx = pFileCtx->pBufCtx;
long size = file_size(p); // ここで-1が返ってくる
if(size == -1){
file_error(pBufCtx->pAppCtx);
return false;
}
}
ここで1回目のエラーが表示される.次にfalseが返った後 do_with_buffer
内において再度
static bool do_with_buffer(BufferContext *p){
MyBufferContext *pBufCtx = (MyBufferContext *)p;
MyFileContext readFileCtx =
{{NULL, pBufCtx->pAppCtx->pFname, "rb", reader}, pBufCtx};
if(!access_file(&readFileCtx.base)){
file_error(pBufCtx->pAppCtx);
return false;
}
}
のようにして file_error
が呼ばれるため2回目のエラーが表示されてしまう.どうも後者のコードは余分なように見える.
そもそも access_file
から呼び出されるユーザー関数はファイルアクセス以外の処理もするはずで,access_file
がfalseを返したからといって一律にそれをファイルエラーとして判定するのは乱暴.かといって access_file
では fclose()
が呼ばれるのでここでのエラーを無視するわけにもいかない.
access_file
関数内の``fclose()`` 呼び出し部分から``file_error`` を呼びだせばよいのだろうか? しかし access_file
は共通関数であり file_error
はアプリケーション,file_error
の呼び出しを access_file
内に埋め込むこともできない.つまり fclose()
がエラーを起こすことは知りたいけれどエラー処理のコードを直接その場所に書くことはできないというジレンマを抱えている.このようにある場所での処理の状況を別の場所から管理したいけれど監視する側と監視される側をお互いに依存させたくない.このような場合にオブザーバーパターンを活用できる.
今回のファイル処理においては
bool access_file(FileAccessorContext *pThis){
assert(pThis);
bool ret = pThis->processor(pThis);
if(pThis->fp != NULL){
if(fclose(pThis->fp) != 0) ret = false; // !!!Candidate1!!!
}
return ret;
}
FILE *get_file_pointer(FileAccessorContext *pThis){
assert(pThis);
if(pThis->fp == NULL)
pThis->fp = fopen(pThis->pFname, pThis->pMode); // !!!Candidate2!!!
return pThis->fp;
}
これらの関数の中でエラーが起きる可能性があるのはCandidate1,2 であるから,これらのエラーを外部に通知できるようにする.まず FileAccessorContext
にオブザーバーオブジェクトを登録できるようにする.