2011/10/24

いまさらVisual Basic(オブジェクト指向篇)

本日は Visual Basic のオブジェクト指向篇です。

とはいえ、今回のお話はどちらかというと Java や C++ の学習によるシナジー効果で身に付けた知識だったりします。『VB しか必要ないから VB だけがんばる』という硬直した方針では、なかなか前に進めないものだなぁと思ったり思わなかったり。

そんなわけで本日は、GoF のデザインパターンの中から個人的に気に入っているものを独断と偏見でチョイスして、実際のソースコードとともにご紹介したいと思います。



【Singleton パターン】

まずは、理解しやすく実装も容易な Singleton パターンからご紹介しましょう。

これは、プログラム内に存在するオブジェクトが 1 つだけである事を保証するための仕組みです。一体それが何の役に立つのでしょうか。その答えの一つが『ゲームプログラマになる前に覚えておきたい技術』の p.183 で述べられていますので、当該箇所を引用します。
シングルトンクラスの目的は以下のようにまとめられる。
  • 1. グローバル変数の危険さを軽減する
  • 2. グローバル変数と同じように使えるようにする
要するに、安全性を高めたグローバル変数のことである。
……だそうです。

実際には、オブジェクトを生成する際に New が実行されるたび、(新たなオブジェクトを格納するための)メモリ領域が逐一確保されます。そのため、このままでは『オブジェクトが 1 つである事』を保証できません。

Singleton クラスでは、コンストラクタを Private にする事によって、外側から New の実行を禁止します。Singleton クラスは、内部的に静的な唯一の自己参照 _instance を持ち、外部から Singleton オブジェクトを要求された場合は、その _instance を返却します。

以下に実装例を示します(展開してご覧ください)。


実行すると、コンストラクタが 1 回しか実行されていない事、および main 内の singleton1 と singleton2 が同じオブジェクトを共有している事が分かります。

Singleton クラスがデータを適切にカプセル化していれば、前述の通り安全なグローバル変数として使用する事ができますし、また、New が重たいオブジェクトを使い回す際にも効果的でしょう。



【Iterator パターン】

多くのオブジェクトを一元的に集約したオブジェクトを作ったとしましょう。その際、その集約オブジェクトが持つ要素すべてにアクセスする統一的な手段を用意するのが Iterator パターンです。

『統一的な手段』とは、「集約オブジェクト内部の実装がどのように変更されようとも、要素のアクセス方法は変える必要がない」というほどの意味です。

以下に、Iterator パターンの VB サンプルコードを掲載します(展開してご覧ください)。

これは、複数の名前を集約管理する名簿(ListOfNames)クラスを作り、それに対してイテレータを追加したものです。


このプログラムは単独で動作しますので、理解したい方はぜひコピペして動かしてみて下さい。

Main の中の While ループで、実際にイテレータを使用して名簿オブジェクトの要素にアクセスしています。

  1. While it.hasNext  
  2.     Dim name As String = CStr(it.getNext())  
  3.     Console.WriteLine(name)  
  4. End While  

よく見ると、呼び出されているのは MyIterator のメソッドだけ ― つまり、ListOfNames の実装が、要素へのアクセス方法に影響を及ぼしていない事が判ります。

Iterator パターンを適用しないと、データ構造が変わるたびに要素へのアクセス方法を切り替える必要があります。

集約オブジェクトの正体がただの配列であれば、For ループで添字アクセスが可能ですが、もし木構造に格納されている場合は、より複雑なトラバースアルゴリズムをコーディングしなければなりません。

しかし、Iterator パターンを用いると、外部に対してデータ構造を隠蔽する事ができます。しつこいようですが、内部の実装がどうなっていようとも、同じ方法で要素を走査する事が可能になるという事が、Iterator パターンの大きな利点です。



【State パターン】

State パターンは、状態に応じて振る舞いを切り替えたいときに真価を発揮するパターンです。

『状態に応じた切り替え』と聞くと、わざわざオブジェクト指向などを使わずとも If 文で単純に処理を分岐させればよいように思えますし、実際それは可能です。

ですが、状態数が増加すると、制御構造が極めて複雑化してしまいます(3月27日の日記あたりを適当に参照)。余談ですが、『ゲームプログラマになる前に覚えておきたい技術』でもこの問題点の解決のために 5 章を丸ごと割いています。

今回は、3月27日の日記をより簡単にした状態遷移プログラムを紹介します(展開してご覧ください)。

これは、State インタフェースを実装した各状態クラス(State1State4)の間を遷移するプログラムです。

これによって、『状態の切り替え』と、『状態に応じた振る舞い』を巧妙に実装する事ができます。

状態クラス State1 を見てみましょう。ここには、『その状態における振る舞い』と、『次状態の情報』のみが含まれています。

  1. Public Class State1  
  2.     Implements State  
  3.   
  4.     Public Sub New()  
  5.         Console.WriteLine("状態1に移りました")  
  6.     End Sub  
  7.   
  8.     ' 状態の更新  
  9.     ' 戻り値は次状態を返すupdateメソッド  
  10.     Public Function update() As State Implements State.update  
  11.         Console.WriteLine("※ ここは、状態1です")  
  12.         Return New State2() ' 次状態は State2  
  13.     End Function  
  14. End Class  

次に、状態遷移を実際に行う main の中身を見てみましょう。

  1. Dim state As State      ' State型の宣言  
  2.   
  3. state = New State1()    ' 初期状態の代入  
  4.   
  5. Do Until (TypeOf state Is State4)  
  6.     state = state.update()  
  7. Loop  

条件分岐のための制御構造がソースコードから排除されている事が判ります。つまり、オブジェクトがどの状態にあるのかを意識する必要がなくなるという事です。

また、遷移先を変更したい場合も、修正箇所は当該状態クラスに限定されるため、ソースコードの更新が容易になります。



【Template Method パターン】

Template Method は、複数の処理を決まった順序で実行させたいときに、その『順序』を前もって定型化しておくパターンです。

あくまで処理順序の『雛型』を作り、個々の具体的な処理は切り離して考える事がポイントです。これによって、柔軟で取り回しの利きやすいプログラムになります。

以下に、Template Method パターンのサンプルを示します(展開してご覧ください)。

これは、プログラムのコメントを自動生成するというものです。コメント生成機能は、VB 用と C 言語用を用意しました。どちらも同じテンプレートを継承している事にご注目ください。

上記のプログラムを実行すると、以下のように表示されます。

VBCommentPrinterCCommentPrinter オブジェクトともに、いずれも printHeader()printMain()printFooter() 関数が AbstractTemplate クラスで定めた順序通りに実行されている事がわかります。

繰り返しますが、基底クラスはあくまで雛型の役割しかしておらず、具体的な処理を一切制限しないという点が重要です。

そのため、よく使う処理の流れだけを前もってテンプレートにしておき、後から具体的に処理を書いていくという使い方ができます。便利ですよ。



【Facade パターン】

そろそろ力尽きそうなので、今日はこれで最後。ラストは個人的にかなり気に入っている Facade パターンのご紹介です。

オブジェクト指向に少し慣れてくると、部品のような細々としたクラスを個別に扱うのが面倒になってきます。なぜかというと、プログラマはそれらの雑多なクラスに関する知識が必要になってくるからです。

そこで、それらをまとめて扱うための『窓口(Facade)』を作り、以後は個々の部品を直接いじるのではなく、その窓口を通して処理を行うようにしよう、というのが Facade パターンです。

サンプルは、掃除、洗濯、調理といったあらゆる家事を行う個々のクラスと、それらのまとめ役であるチーフ(これが Facade になります)を作ります(展開してご覧ください)。

このプログラムでは、チーフオブジェクトが掃除屋、洗濯屋、料理人といったすべてのオブジェクトを一元的に管理してくれるため、(main の中身を書く)プログラマは、ごちゃごちゃしたオブジェクトを意識する必要がなくなります。これが一番の利点です。

そして、チーフにアクセスしている(外部の)オブジェクトは、(チーフが管理している)個々のオブジェクトに対して直接的な依存関係がありませんから、それら雑多なオブジェクトのいずれか一つが変更されても、影響を受ける範囲を制限する事ができます。

このパターンは比較的簡単ですから、パターンを学ぶ前から当たり前のように行っていた方も多いと思います。



【てきとうあとがき】

なんかちょっと消化不良ではありますが、VB のための GoF のパターンをいくつかご紹介してきました。

個人的には、Adapter パターンや Strategy パターン、Composite パターンや Abstract Factory パターンなどについても、実コードを交えつつ紹介したかったのですが、使用頻度的にそれほどでもなかった事と(※当社比)、これ以上は普通に面倒だったので挫折しました。

ただ、概念レベルで見れば、この日記で紹介したパターンに非常に近いパターンがいくつもありますので、とりあえずよく使いそうなものから身に付ければどんどん習得が容易になっていくのではないかなと思います。

それでは、今日はこのへんで…。



【本日の参考文献】
  • Erich Gamma, Ralph Johnson, Richard Helm, John Vlissides, 『オブジェクト指向における再利用のためのデザインパターン』,ソフトバンクパブリッシング,1994.
  • 結城浩,『Java言語で学ぶ デザインパターン入門』 ,ソフトバンクパブリッシング,2001.
  • 平山尚,『ゲームプログラマになる前に覚えておきたい技術』,秀和システム,2008.

0 件のコメント:

コメントを投稿

ひとことどうぞφ(・ω・,,)