REALbasic編


デザインパターン

REALBasicで本格的にアプリを作る前に、JAVAを勉強しているときにちょっとかじった
デザインパターンをRBで作っておこうとしました。2、3日で終わるだろうと考えて
いたのですが、これがいろんな罠が待ち構えていてすでに1ケ月が経過、しかもまだ
解決できていないものが、いくつも有るという状況になっています。

最初は掲載する予定ではなかったのですが、これだけ苦労したものは、きっと他の人の
役に立つこともあるだろうと思い掲載することにしました。

RBのバージョンは 2.1.2 です。プロジェクトのみです。出来たものから順次掲載します。
なお、元にしたJAVAのサンプルコードは結城 浩さんの 『Java言語で学ぶデザインパターン』
の中に出てくるソースを勝手に移植させてもらっています。この本にのっているソースと
プロジェクトのソースを見比べてみるのも面白いかもしれません。

Exceptionの処理はまったく入れていません。なぜかというと僕がよく理解できてないからです。
本当はいれたほうがいいです。
-----追記2001/11/30---------------------------------
InterpreterパターンよりExceptionの処理を入れてみました。

(注意)ここで解説していることは、勘と推測で行っているものであり、なんら裏をとって
いないことを明記します。つまり、確証は自分で得てね!ってことです。
-----追記2002/2/9------------------------------------
ま、すでに終わっているコーナーですが一応追記します。

えー、いま見直すと記述のまちがっているところが多々ありますが
やる気がないので直しません。

作った順番にならべてますので、クラスなんかは後ろの方が前の方に
くらべればまともに作ってあります。(自分比)

てなわけで、あまり信用しないように。

Runnableインターフェイスはうまいこと出来ましたので、また
やる気が出たら掲載します。

Iterator

まずはIteratorからですが、ぱっと見こんなもの必要ねえよって思います。
が、Iteratorなパターンは今後頻繁にでてきますし、何回か使ってる内に
いいやんこれって思ってきます。結構重要であるでしょう。

ここでのポイントは、Javaでいう this はRBでは me だということと、
Javaで使う Object型 はRBでは Variant型 を使うことになるというこ
とです。

そして一番大きいのは、RBではキャストする必要がないのです。
Javaならば Book book = (Book)it.next(); という様にキャストが必要
ですがRBだと book = it.nextObject という感じで、キャストしなくても
いいわけです。これは僕の勘ではどうせその宣言されたクラスにキャスト
するのは見え見えだから、省いておいたよって言ってる様に感じます。
良いのか悪いのか....。(RBではnextは予約語でメソッド名には使えないので、
nextObjectという名前にしてます。)

ついでに、Collectionクラスを継承しイテレータブルにした、その名も
Vectorというクラスをつくっておきました。これが後々頭を悩ませる
種になっていきます...。
-----追記2001/11/20---------------------------------
この悩みは解決しました。Visitorの説明に詳しく書いてます。

Template
Method

テンプレートメソッドパターンです。ここではとりたててポイントはありません。

Factory
Method

ここでのポイントは、メソッドを呼ぶ時はキャストが使えるということですね。
owners.add IDCard(product).getOwner の部分ですね。

また、上位クラス(又はインターフェイス)を下位クラスにキャストするのは
いいのですが、下位クラスを上位クラスにキャストしようとすると
コンパイル時にエラーが出ます。

これはRBの仕様が、Abstract指定を使えないという点と、インターフェイス
を実装したクラスはメソッドを無視できないということから、下位クラスは
必ず上位クラスのメソッドを使えるので、キャストの必要はなく、
コンパイルではエラーにしているという感じです。

Singleton

RBにはStatic指定がないので、厳密なSingletonはつくれません。

またSingletonにするためには、Singletonインターフェイスを実装
するという制約をつけました。

instance = (new Classname).getInstance という生成の仕方を
すれば、同一オブジェクトを参照するという所までしかできません。

object =new Classname とされたらSingletonにはなりません。

Builder

RB2.1.2はメソッドの引数に配列を渡せないので、StringArrayの様に
クラスにしてしまいます。

Javaでの改行文字 \n はRBではchr(13) かchr(13)+chr(10)を使うん
だと思います。

Brige

RBはメソッド名の大文字小文字は判別するのでしょうか?
たぶんしないと思われます。次ぎの一文がそれをあらわして
います。

self.Display(impl) これはselfつまりスーパー
クラスのDisplayというメソッドをimplを引数に渡しています。

戻値のないメソッド(つまりSubメソッド)を呼び出すときは、
通常self.Display impl という形になりますが、メソッドが
オーバーロードされている場合、カッコでくるむ必要がある
場合があるのを経験しています。

で、ここではDisplay(impl)というコンストラクタと、
display()というメソッドを持っています。
RBがこれをオーバーロードされたメソッドだと認識して
いるのではないかと思います。

このサンプルプログラムでは、たまたまnewが引数ありで
指定されているため、問題がでなかったまでです。

本来なら、コンストラクタ以外のメソッド名はクラス名
と違う名前にするべきなんでしょうね。

ちなみに、self.Display(impl)をJavaで書くと
super(impl);になります。

Decorator

ここではオブジェクトのメソッドを呼ぶ前に、me.をつけるかどうかで
結果がえらく違ってくるという現象がおきてます。

理由は謎です。参照が深くなるとこの手の現象が出てくる感じがします。
これは、新しいバージョンでは直ってるんでしょうか?

Facade

Javaのソースに合わせるように、WriterクラスとPropertiesクラスを
作りました。ほとんどテキトーに作ってあるので気をつけて。

Propertiesクラスでは、明日夢さんのPluginを使わせて頂いてます。

Databaseクラスのように、まるごとStaticなクラスはRBではあっさり
モジュールにしてしまうのも手です。

State

RB2.12のウインドウは、クラス継承もインターフェイス実装もできないので
SafeFrameクラスはウインドウとクラスに分離しました。

mainクラスのThreadはウインドウのTimerコントロールに肩代わりさせてます。

Visitor

さて、こいつに1ケ月悩まされています。このパターンではなんとか
妥協作を作れましたが、同じようにCompositな構造のあるAbstractFactory
パターンでは、まだ解決ができません。

これがミソなのになあ....

---追記 2001/11/20-------------------------------------------
なんと1ケ月も悩んでいた問題が単純なことであっさり解決してしまいました。
DirectoryクラスのgetSizeメソッドの中の、dim entry as Entry の部分が
問題でした。これを dim entrys as Entry に直すだけでうまくいきました。

ああ、俺ってアホだ..。メソッド内変数をクラス名と同じものにすると、
Iteratorを使ったCompositな処理ではRBが混乱をおこすようです。
普通の処理ならなんら問題ないんですがね..。これでIteratorまたはVectorの
信頼性が確認できてよかったよかった。

これにより、Composit、AbstractFactoryも同時に解決。

Chain of
Responsi-
bility

JavaのクラスはデフォルトでtoStringというメソッドをもっています。
JavaのコードをRBに移す場合、string =classname みたいな文がある場合
RBではstring = classname.toStringという感じに直さなくてはいけないかも。
もちろんtoStringメッソドは実装してね。

あと、RBはメソッドの戻値を無視できないので、supportメソッドはDumyで
戻値を受け取る必要があります。

Obserber

以前、RBのウインドウは継承もインプリメンテーションもできないと
書きましたが、勘で言ってるので、ホントはどうだか解りません
が、そういうことにして、ウインドウ とクラスが分離された場合に
そのやり取りにObserverパターンが使われるのでしょう。

Composit

Visitor問題の解決によりCompositも自動的に解決。

AbstractFactory

Visitor問題の解決によりAbstractFactoryも自動的に解決。

親クラスのコンストラクタを呼ぶ場合クラス継承が2層ならば
以前書いたように、self.SuperClassName param のような書き方でも
いいのでしょうが、このパターンのように3層になる場合、selfは1つ上
のクラスを見るのか、それとも階層の一番上を見るのかがよくわからない
ので(僕の勘では一番上を見ると思う)、selfをはずして
SuperClassName param という形にしてやるのが安全かもしれません。

self付の方が明示的で好きなのですが、しかたありません。

以前書いたように、RBはAbstractは使えないので、下位クラスは上位クラス
のメソッドをすべて、コンストラクタさえも普通のメソッドとして継承します。

ですから、メソッドがオーバーライドされていない限りはselfをつけても
つけなくても結果は同じということになります。

また、下位クラスは上位クラスを必ず知っているので(継承してるから
あたりまえですが)、Javaのように super(); という抽象的な呼び方でなくても
コンストラクタメソッドを直呼びしてもそんなに問題はないような気がします。

RBで文字列からクラスのインスタンスを生成する方法が見つからなかったので
それはモジュールでベタな処理でごまかしました。ver3.5のRBScriptって
それができるんでしょうか?だったら買ってもいいかななんて、ほんの
ちょっぴりですが思いました。

Interpreter

このサンプルのために、 StringTokenizerを作りました。実行効率は
疑問ですが、一応動きます。

これを作っていて、自分なりの安全なRBの組み方というものを見つけました。

まず前提として僕は、いちいち変数名を考えるのがめんどくさいのです。
変数名はクラス名の頭を小文字にしたものを使いたい、その上でどう安全に
組むか、という話になります。

1..プロパティに登録するオブジェクトを参照するときは、必ず頭に
  me. をつける。
2..メソッド内ローカル変数にクラス名と同じ名前をつけるならば、最後に
   s をつける。
  (これは別になんの文字列を使ってもいいのだが、僕はsをつけたというだけ)
3..引数で渡されるオブジェクトはそのまんまでよい。
  (なぜかというとオブジェクトは参照渡しだから、厳密に言うと
  オブジェクトのポインタがコピーされてくる。(勘です))

Exceptionの処理を初めて組み込んでみましたが、こんなもんでいいのかな?

JavaだとtoStringメソッドはObjectクラスの段階で付いてるので、Vectorに
対してtoStringしても安全なんだろうけど、RBの場合保証ゼロなので
Vectorに対するtoStringはCommandListNodeクラスに移しました。

Mediator

ButtonやEditFieldのようなControlを継承したクラスは、コードから
インスタンスを作れないので、あらかじめウインドウのなかに作っておいて
その参照をメインクラスのプロパティに格納し、操作します。

ウインドウのなかのコントロールどうしが直接メッセージのやりとりを
するような書き方はしないわけです。

Command

おもわぬ落とし穴というものがあるもんで、いつものくせで
for i= lastsize to 1 step -1と書いてしまい悩んでしまった。

ほんとの書方は
for i = lastsize Downto 1 step 1 が正解でした。

上記はVectorにClearメソッドを仕込むときにひっかかりました。

FlyWeight

Javaでつかうsyncronizedはマルチスレッド環境で共有される
コードを同期化するために使います。Rbで同じことを実現するためには
CriticalSectionクラス(又はSemafore)を使います。

Javaではクラス単位の共有になるのでクラス内の任意の場所で
syncronizedを始められますが、Rbの場合CriticalSectionの
サブクラスとして作ったインスタンスを共有し同期化することに
なります。

なので、その部分のコードはCriticalSectionのサブクラスとして
分けて作ることになります。また、そのインスタンスを共有する
形になるので、おのずとSingletonパターンになります。

FlyWeightのサンプルでは、この効果がわかりにくいので、
syncronizedTestというプロジェクトも作っておきました。

なおBigCharのテキストファイルは0,1,2を作ってから
めんどくさくなり、作ってません。

Strategy

Javaではメモリ上にクラス領域というのが作られるらしいので、
new しなくてもクラスメソッドやプロパティにアクセスできます。

Rbはそんなことはできないので、いつもならstaticなものは
モジュールにしてしまいます。

ですがこのパターンみたいにクラスのプロパティ(Javaではフィールド
と呼びます)に、自分のインスタンスを保存してその参照を渡すといった
場合は、モジュールをつかうと結局クラスもつくらなければいけなくな
り、1つのことを2つで管理する形になっちゃいます。

このへんは好みの問題なのでしょうが、やっぱ1つのクラスのなかで
完結させたほうが楽かなと思います。

そうすると、やっぱりSingletonパターンを使ちゃいます。
で、僕のつくったSingletonは効率がわるいので困っちゃう訳です。

Adapter

とくにポイントはございません。

Proxy

とくにポイントはございません。

Menento

スレッドのスリープとかはRbではどうすりゃいいんでしょうかね?

まず、RunnableインターフェイスみたいなものがRbにはないんです。

JavaもRbも単一継承・複数インターフェイス構造なので、ほぼ
同じ考え方でプログラミングをできるわけですが、基本仕様の
違いからか、いくつかそのまま流用できないものもあるわけです。

自前でRunnableインターフィス作って実験してみたいけど、
時間がなくてねぇ...

Prototype

これもRbではそのま使えないものの一つですね。

ただし、JavaのCloneの解釈にはまったく自信がないのですが、
とりあえず、デザインパターン編を仕上げたいので無理から
やります。(多分後に補足を書くことになるでしょう。)

(注意!これ以降はホントに確信めいたものもないまま話し
をしますので、これ以降の内要は鵜呑みにしないで下さい)


JavaのCloneは浅いコピーなので、値は値のままオブジェクトは
オブジェクトのまま(つまりオブジェクトのポインター)をコピー
してるだけになります。Rbでいえば自分のクラスのインスタンス
をNewして、自分のプロパティをそのまま一つずつコピーする
だけです。

ただし、ここで問題が一つ。Rbのプロパティのアクセス権です。

Rbのプロパティのアクセス権はpublicかPrivateのみです。
RbにはパッケージとかProtectedとかの考え方がないので、
たとえ、自分のクラスをNewしてプロパティにアクセスしても、
それが Privateならばまったく見えません。
かといって、すべてPublicにしてしまうのもイヤーンでしょう。

しかた無いのでアクセスメソッドを作ってやる訳です。

(僕の)浅いコピーの定義にしたがえば、Collectionのオブジェクト
にプロパティを一つずつaddして、受ける側はそれを一つずつ
自分のプロパティに代入するという作業をします。

(時間がないので、JavaのCloneがほんとにそうなのか
検証するひまがありません、でも近い線ではあると思い
ます)