ホーム < ゲームつくろー! < デザインパターン習得編
Template Method
〜過程をたくさん変更して柔軟な結果を出そう
@ 次はあれして、その次はこれして・・・
色々なことをたくさんしなければならない1つとして「初期化」が挙げられます。例えば、DirectXの初期化はいくつかのパートに分かれていて、それを1つ1つこなしていく事によって、初めて使用できるようになります。
この初期化作業は、大筋は同じだけど部分部分でちょっと異なるという事が良くあります。そういう場合の対処方法の1つはswitch〜case文で条件分けをする方法です。しかし、冗長になってしまうのは、いうまでもありませんし、第一柔軟性に欠けます。そこでオブジェクト化と行きたいのですが、あまりに小さなオブジェクトですし、使い道が限定しすぎるという事もあります。
そういう時に使えるのがTemplate Methodパターンです。
このパターンは、何らかの結果を出すための「過程」を含んだ関数があり、その関数内で使用されるサブの仮想関数群を派生によって変化させることで、柔軟な結果を生み出すパターンです。
A サウンドプレーヤーをTemplate Methodで
例として、サウンドプレーヤークラスを考えてみます。サウンドプレーヤーで音楽を再生する場合、
1. 曲を選ぶ(サウンドファイルのオープン)
2. 曲のデータ構造を解析する
3. 監視用のスレッドを始動させる。
4. 再生する
という順序で細かく制御する必要があります。これをSoundPlayerクラスのPlay関数内で行うとすると、
1. OpenFile()
2. AnalysisFile()
3. CreateObserveThread()
4. PlaySound()
というサブ関数を並べてそのプロセスを経ることになります。
class SoundPlayer
{
private:
char* m_pSoundData; // サウンドのバイナリデータ
int m_Size; // サウンドのサイズ
char* m_Filename; // サウンドファイルの名前
private:
virtual bool OpenFile(char *filename); // サウンドファイルのオープン
virtual bool AnalysisFile(); // サウンドデータの解析
virtual bool CreateObserveThread(); // 再生監視用のスレッドを生成
virtual bool PlaySound(); // サウンドの再生
public:
bool Play(char *filename){ サウンドを再生する
if(OpenFile(filename)) return false; // ファイルオープン
if(AnalysisFile()) return false; // ファイル解析
if(CreateObserveThread()) return false; // 監視用スレッド生成
if(PlaySound()) return false; // 再生
}
};
Play関数は、再生に必要な手順を並べており、これが「テンプレート」となっているわけです。それぞれの関数の役目は親クラスでデフォルトを定義しても良いですし、派生クラスで別機能を持たせるのも良いしょう。
例えば、wavファイルを再生できるSoundPlayerWavクラスを派生して作るとします。この場合、OpenFile関数で指定のファイルをオープンし、次にAnalysisFile関数でそれがwavファイルかどうかを判定します。wavファイルだったら、再生に必要なデータをすべて抜き出します。CreateObserveThread関数は、ストリーム再生を行うために必要なスレッドです。最後にPlaySound関数でサウンドデータの最初から再生を行います。
同様に、MP3を再生するSoundPlayerMP3クラスなども(内部構造がわかれば)作成できます。
テンプレートパターンのクラス図を示します。
サウンドプレーヤークラスに置き換えると次のようになります。
B Template Methodパターンはリファクタリングの1つ
このパターンは、クラスのある振る舞いをサブ関数で細かく制御しています。これは、「リファクタリング」の1つといえるでしょう。リファクタリングとは、一度作ったプログラムをシンプルで最適な形に再構築することで、プログラムの保守管理における重要なプロセスの1つです。
クラスを最初から綺麗な設計にすることは難しいものです。しかし、リファクタリングをすることによって、クラスはだんだんと美しく、再利用性の高いものに変化していきます。Template Methodは、クラス内の関数の振る舞いをコードレベルで分断してサブ関数化しています。これは、その部分が少し変化する可能性があるためで、それによってクラスの再利用性が格段に高くなります。よって、派生する必要の無い部分まで関数化するのは、リファクタリングとは言えません。それは単にコードを見やすくしたに過ぎません。
Template Methodパターンは、他にも画像ファイルの読み込みなど、大きな仕事をするクラスで使用できます。積極的に使用して、振る舞いをパターン化してしまいましょう。