RealBasic University

このコラムはStone Table Softwareのオーナーであり、またREALbasic Developerの編集者でもあるMarc Zeedar氏により書かれたものを、著者の許可を得て翻訳したものです。この翻訳はHREM Researchにより提供されています。この日本語版へのご意見はRBU-Jまでご連絡下さい。

URL: http://www.applelinks.com/rbu/095/
INDEXに戻る

OOP University:パート 19

SuperDrawシリーズの後、皆さんの記憶をリフレッシュして我々全員が同じスタート地点に立つために、先週はいままで取り上げてきたOOPの概念の復習をはじめました。私はカプセル化の重要性を皆さんに指摘しました。今回は、SuperDrawが我々に教えてくれたものを引き続き検討して行います。

isa

OOPでもっとも強力な機能のひとつが、isaです。我々はSuperDrawの一部で、どのオブジェクトが渡されたかを検出するときに、それを使用したことを覚えているでしょう。これは、継承(inheritance)の二次的な利点です。技術的に言うと、サブオブジェクトは依然として、その親クラスのメンバーであるので、ルーチンのパラメータとして包括的なオブジェクトを渡すことができます。

例えば、メインクラスとしてanimalClassを、サブクラスとしてcatClassdogClassがあるとしたら、次のようなルーチンも可能です。

  
sub aRoutine(theAnimal as animalClass)
if theAnimal isa catObject then
// bla bla bla
elseif theAnimal isa dogObject then
// bla bla bla
elseif... // etc.
end sub

これは、ルーチンが猫のオブジェクトの操作だけに限定されるのではなく、すべての動物について操作できるため、強力なものです。isaは、必要であれば、我々が現在取り扱っている動物を正確に見つけ出す機能をルーチンに与えてくれます。(ルーチンが動物に共通のプロパティを変更あるいは検討しているだけならば、動物の種類を検出する必要はないことを覚えておきましょう。)

isaについて忘れることの無いようにしてください――それは、あなたがオブジェクト指向アプリケーションを設計しているときは、留意すべき重要な概念です。多くの場合で、isaを使うことで、あなたは設計を簡略化することができます。

OOPアプリケーションの構築

OOP Universityの一環として、我々はオブジェクト指向の描画プログラムであるSuperDrawと呼ばれるプログラムを作成しました。このプロジェクトはSimpleDrawとして開始され、その後で我々はオブジェクト指向の方法を使用してより多機能のSuperDrawにプログラムを拡張・強化するのがどれほど容易であるかを示しました。

なぜこのことについて述べるのでしょうか?なぜならばそれは重要だからです。お分かりのように、OOPの利点は、プログラミングを容易にするのではありません。 とりわけあなたがオブジェクトに慣れていない場合、OOPはさらに難しくなることでしょう。実際に、オブジェクト指向アプリケーションを適切に構築するには、充分な計画と設計が必要とされます。

そうではなく、OOPの利点は、再構築、書き換え、拡張、あるいはプログラムのデバッグのときに表れます。このことを明らかにしましょう。

OOPの利点は、再構築、書き換え、拡張、あるいはプログラムのデバッグのときに表れます。

分かりましたか?それが意味することは、あなたが「一度限り」のプログラム(一度に完成するような簡単なプログラム)を書いている場合は、それをオブジェクトで構築する努力はおそらく無駄になります。

例えば、今年初めに私の会計士が、REALbasic Developerマガジンについてのある種の財務資料を必要としていました。この情報は、様々なデータについてある数学的操作が必要だったため、データベースから寄せ集めるのが困難でした(ある総計は繰延課税で他はそうでないことから、未配達物の総量と配送料を、項目別として取り出すことを私は余儀なくされました)。よって私は、生データを簡単に解析し、私が必要な情報を抜き出して、その後の計算のためにスプレッドシートへ取り込めるタブ区切りのレポートを生成する「一度限り」のプログラムを作成しました。これは一度しか使う予定のないプログラムでしたので(あるいはせいぜい一年に一度)、それが適切なオブジェクト指向の設計であるかについては問題にしませんでした。

OOPが効果を発揮するのは、長期間に渡るプロジェクトにおいてです。拡張、書き換え、そして機能を追加する予定のあるこのような種類のプログラムでは、OOPではそれは非常に容易に行えます。我々がSuperDrawで見てきたように、基本のオブジェクト指向アプリケーションを一度設計してしまえば、新しいオブジェクトや新しい機能を追加することはとても簡単でした。しかし、最初のオブジェクト指向アプリケーションを作成するには、我々がとる方向についていくらかの設計を必要としました。

私は次のように、OOPと従来のプログラミングの展開を反対向きの三角形として捉えています。

従来のプログラミングは、設定や計画をあまり必要とせず、そのため動作するプログラム(完成したものではなく)が容易に作成可能です。オブジェクト指向プログラムは、オブジェクトがお互いにどのように機能するかを設計し、すべてのクラスを書くことを意味します。ある時はアプリケーションが実行される前にこれらのクラスは相互作用しますが、そのときには、ほとんど完成です。そしてもちろん、オブジェクトの動作を修正したり、拡張することは簡単です。

オブジェクト指向プログラムは、再利用を考慮に入れてオブジェクトを作成しなければならないので、最初は書くことが難しくなります。これは、将来のプロジェクトについて前もって多くのことを考え、時にはオブジェクトを「上書き」する(現在のアプリケーションで必要なものよりさらに頑丈、強力で一般的にする)ことを意味します。しかし賢いプログラマーは、今日の余分な数分間が、将来のプロジェクトの速やかな発展を意味することに気づくでしょう。

ここに手軽で、とてもシンプルな例題があります。あなたがプログラムを作成していて、数値のみを受け付ける文字入力欄(数値だけのeditField)が必要であるとしましょう。これは全体のプログラムであなたが必要とする唯一の所ですので、あなたがまず考えることは、その特定のeditField内部に単純にこれを書くことです。しかし待ってください、これはあなたが書く他のプログラムに対して役に立つクラスとなるに違いありません。

ここに、「数値のみ」の問題を解決する3つの方法があります。

お分かりのように、それぞれはその前のものよりも作成するのにより時間がかかります。しかしながら、より強力にもなり、将来時間を節約できるでしょう。今日手軽に行うか、将来の時間を節約するのが良いかはあなた次第です。

私はプログラムがオブジェクト指向でないことにフラストレーションを感じるRBプログラマーからEメールや電話をもらいます。中にはそれが何を意味するのかさえ確かでない人もおり、またOOPの方が優れているので、プログラムはオブジェクト指向にする必要があると単に思い込む人もいます。私が言いたいのは、すべてのプログラムがオブジェクト指向である必要はないということであり、またオブジェクト指向プログラムは、時に初心者が難しいと感じる初期設計に多くの努力を要するということです。

OOPの要所は設計です。我々が次に取り上げるものは、オブジェクト指向にするため、アプリをどのように設計、計画すればよいかについてのいくつかのアイデアです。我々はいくつかの典型的なプログラムを検討し、オブジェクト指向とオブジェクト指向でないバージョンを比較します。

次週

オブジェクト指向プログラムを設計します。

Letters

今回は、文字列を渡すことについて質問があるMyron Tribusさんからお手紙を頂きました。彼は次のように書いています。

私は動作する2つのプログラムをすでに書き、3つ目に取り掛かっています。私は以前に、1964年頃からダートマス大で開発されたTrueBASICでプログラムをしたことがあります。これはおそらくハンディキャップです!とにかく、私はTrueBASICでサブルーチンを呼び、例えばそれを文字列に渡すことができます。ところが、私がファイルを取り込み、例えばSという文字列にそれを代入し、他のボタンを押した結果としてこの文字列に何か操作をしたい場合、新しいオブジェクトにどのようにSを渡すのでしょうか?オブジェクト指向プログラミングの利点は、お互いの要素が独立であることです。オブジェクト間で情報を共有するにはどうすればよいのでしょうか?私は、他のオブジェクトが利用できるリストボックスへその文字列を入力することを考えましたが、もっといい方法があるはずだと思います。

あなたのチュートリアルはとても役立ちます。

Myronさん、ご質問ありがとうございます。私はあなたの問題が分かると思います。文字列についての何か特定のものというよりも、それはスコープ(scope)に関係しています。

スコープは、初心者が非常に混乱する概念です。基本的には、変数はコードのあるセクション内で「有効(alive)」あるいは可視(visible)のどちらかのみです。オブジェクト指向プログラムでは、それはあるオブジェクト内だけを意味します。例えば、window1theStringのプロパティを持つとすれば、そのウィンドウの外部の他のルーチンには変数theStringは存在していません。この変数はwindow1の一部であり、window1内部のオブジェクトやコードに対して可視であるだけです。

これはローカル(local)変数として知られています。すなわち、それはその周りのコードに対してだけ可視です。ウィンドウのプロパティよりも、もっとローカルなものがあります。サブルーチンの一部としてあなたが定義する変数は、そのサブルーチンに対してローカルです。つまり他のコードはその変数にアクセスできません。他にとって、それは存在していません。

グローバル(global)変数は、アプリケーション全体に対して存在するプロパティです。アプリケーションのどの部分も、それらを参照し、アクセスすることができます。

では、なぜ違うのでしょうか?ある変数はサブルーチンに対してローカルであり、あるものはオブジェクト(ウィンドウのような)に対してローカルであり、他はグローバルであるというようなややこしい違いに思い悩むのはどうしてでしょう?

この質問に対する答えは、いくつかあります。一つは、グローバル変数はローカル変数ほどメモリー効率が良くないことです。ローカル変数は、ルーチンが実行中あるいはオブジェクトが有効である間だけに存在します。また、ルーチンが実行されなければ、使用されない変数にRAMを割り当ててメモリーを浪費することがないので、ローカル変数は効率的です。

ローカル変数が良いとされるもう一つの理由は、それがプログラムをより良くデザインするための助けとなるからです。例えば、ファイルの内容を処理するウィンドウにとって、そのデータを含むデータ構造を保持することは論理的です。あなたが複数のウィンドウを持つ場合、各々は独自のデータと一緒に独自の変数設定を含む分離したオブジェクトです。代わりに、あなたがグローバル変数にデータを代入する場合は、あなたは一度に一つのファイルしか開けないことを強いられる(グローバル変数が一つしかないので、一度に一つのファイルのデータに対するスペースしかありません)、あるいは何らかの方法でグローバル変数に動的にデータスペースを追加(例えば配列を使用して)しなければならないでしょう。それは本当に複雑になるでしょう。例えば、ユーザが九つのファイルを開き、中間の一つを閉じる場合、あなたはグローバルなデータ構造を監視し、中間の一つを消去しなければなりません。それではあなたが行うデータ管理が膨大となります。何のためにそうするのでしょうか?あるウィンドウが別のウィンドウの内容を見ることができる意外に、何の利点も得ません。ほとんどのマルチ・ドキュメントのプログラムでは、それは必要でなく、望ましいものでもありません。(あなたがもし2つのワープロのファイルを開いたとき、ドキュメントAがドキュメントBに影響を与えるような編集を希望するでしょうか、または必要でしょうか?)

あなたの状況では、あなたはいくつかの選択肢があります。

一つは、あなたはs(あなたのファイルの内容を含む文字列)をグローバルにして、プログラムのどこからでもアクセスすることができます。それは、そのデータを他のどこにも移動する必要がないことを意味します。

2つ目は、プログラムの一部分から別の場所にデータを渡すことができます。例えば、window1にデータがあり、window2が同じデータを必要とする場合、そのデータを保持するwindow2のプロパティを作成し、それに次のように代入します。

  
window2.theString = s

このコードは、window1内のどこかに置かれ、文字列をwindow2にコピーします。

また次のように、そのデータを別のウィンドウの一部、あるいはプログラムの別の一部分である手続きや関数へ渡すこともできます。

  
DoSomethingWithTheString(s)

3つ目は、少し複雑ですが強力です。あなたはREALbasicのドット構文を用いて、別のオブジェクトのデータに簡単にアクセスできます。我々は上記の例題ですでにそれを使っています。あなたがコンパイラーにそれがwindow1の一部であることを伝えれば、Window2window1の一部である変数sを「見る」ことができます。

  
dim temp as string

temp = window1.s
// bla bla bla

ここで我々は、window1の変数sをルーチンのローカル変数tempにコピーしています。Tempはこのルーチン内でのみ存在しますが、それが存在している間はファイルの内容を保持していますので、あなたが必要なことはどのようなことでもそれを使って処理することができます。

しかし、プログラムの実行中に動的に作成された複数のウィンドウがあるとしましょう。例えばこれは、ユーザがプログラム中で複数のファイルを開くときに起こります。さて、あなたはどれがあなたの欲しいデータを含んでいるのか分かりません。そこには一つ以上のウィンドウがありますので、それはwindow1ではないかもしれません。(プログラムは動的にwindow1のコピーを作成しましたが、プログラム的に[コードで]window1を参照することは、一番始めのものを指すだけです。)

これに対する解決策は、適切なウィンドウを指すプロパティを作成することです。例えば、window2は次を含むでしょう。

  
parentWindow as window1

いま、window2window1.sの代わりに、parentWindow.sを参照することができます。しかしこれが機能するには、あなたはparentWindowが有効である(nilでない)ことを確認しなくてはいけません。そうしなければ、プログラムはクラッシュするでしょう。

ParentWindowは、あなたがそれをwindow1を示すように変更するまでは、最初は常にnilでしょう。

これは少し複雑ですが、動的なオブジェクトがグループとして共に機能することを可能にする極めて一般的な方法です。Object1はリンク(第二のオブジェクトを指すプロパティ)を通じて、object2について知ります。

ここに実用的な例題があります。テキスト編集のアプリケーションがあるとしましょう。それには検索・置換のダイアログ・ボックスがあります。あなたは、ダイアログ・ボックスへテキスト・ファイル全体を渡し、それに修正を加えたテキストを返させることが可能でしょう。しかし、テキストのコピーが2つ存在するので、それは多くのメモリーを浪費します。

もっと効率的な方法は、親ウィンドウ(テキスト・ドキュメント)への参照、あるいはデータ・オブジェクトの参照をダイアログへ渡し、データを直接処理することです。その結果、テキスト全部の複製ではなく、参照だけが渡されます。

重要な要点は、ポインタ変数を初期化することです。ポインタが有効で、正しい親オブジェクトを指さなければいけません。これは機能しなくなります。ポインタを利用するコードは、そのデータを処理する前に、ポインターが有効であるか確認しなければなりません。私が述べたように、少し複雑ですが、単純なグローバル変数より更に効率的です。

これは複雑なテーマです。あなたの疑問点を少し明らかにするお手伝いができたことを望みます。

私はまた、度々言われていますが依然として多くの初心者が気がつかないことについて補足しておきます。それは、REALbasicでグローバル変数を作成するには、モジュールにそれをプロパティとして単純に追加するだけです(ファイルメニュー、「新規モジュール」)。モジュールの一部である定数、メソッド、あるいはプロパティのいずれも常にグローバルで、あなたのプログラムのどこにからでも利用可能です。


RBU-Jの通知サービス!コラムが発表されるたびに日本語版REALbasic Universityのお知らせの emailがあなたに届きます。登録・削除は ここ から。

INDEXに戻る