RealBasic University

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

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

OOP University:パート 18

はじめに、SuperDrawが一段落しましたので、記憶をリフレッシュするために今まで学んだ概念の復習を簡単に行いましょう。皆さんがすべてを理解していることを確認したいと思います。

今後のコラムでは、考慮すべきいくつかの新しい概念を紹介し、より良いオブジェクト指向プログラムを作成するためにはどうすればよいか、提案とヒントについていくつか検討していきます。最後に、今まで議論した多くの概念を説明する例題プロジェクトを作成します。

OOPの復習

モーダルとイベント駆動プログラム

OOP Universityのはじめの数レッスンは、モーダルとイベント駆動プログラムの重要な違いについて指摘しました。これは今ではほとんど議論されない問題ですが(現在のすべてのプログラムはイベント駆動型です)、大変重要な問題です。イベント駆動プログラムは必ずしもオブジェクト指向型であるとは限りませんが、オブジェクト指向プログラムはイベント駆動型に適しています。

記憶をリフレッシュしましょう。モーダル・プログラムでは、プログラムは現行の「モード」をコントロールし、そのモード内では限られたコマンドしか受け付けません。イベント駆動プログラムは、プログラムはユーザによってコントロールされることから、しばしば「ユーザ駆動」プログラムと呼ばれます。イベント駆動プログラムは、プログラムがどのような状況にも対応できなければならないので、モーダル・プログラムよりも作成する事が難しくなっています。

ところが、いくつかの基本関数をすでに取り入れた再利用可能なオブジェクトを作成することが可能なことから、それはOOPにとって理想的なものとなります。もっとも良い例は、ウィンドウ・オブジェクトです。REALbasicで新しいウィンドウを追加してみれば、あなたがプログラムをしなくても、ウィンドウの移動、拡大、サイズ変更、閉じる動作は、ウィンドウ自身が知っています。そのウィンドウがどのように描画されるかという基本部分と、そのウィンドウ・ユーザ・インターフェース(サイズ変更、最小化など)動作は、あなたがプログラムすることなしに機能します。

OOPは、イベント駆動プログラミングをとても簡単にするので、OOPの概念の多くは、イベント駆動プログラミングとしてひとまとめにされます。従来のプログラムに慣れた人々は、イベント駆動型の特徴をOOPと取り違えて(逆の場合も同様に)、この2つをよく混同します。イベントそれ自身は、オブジェクトとは無関係であることを心に留めておいてください。オブジェクトのほとんどは、イベント(メッセージ)を通して交信しますが、あなたのアプリケーションの構造上の問題は、オブジェクト指向の設計上の欠陥よりも、たいていはモーダル・プログラミングに関係しています。

オブジェクトの基礎

次に続くレッスンでは、オブジェクトを紹介します。オブジェクトの概念(任意のカプセル化された構造)、そしてもっとも重要なクラス(class)とオブジェクトの違いを踏まえ、それを説明していきます。

次は、オブジェクトについて覚えておきたい3つの重要事項です:

この3点を理解することは重要ですので、よく理解しておいてください。

オブジェクトの系統化

ここでのOOPレッスンは、オブジェクトにものを実行させる機能を与えることについてです。それは、クラスに適切なメソッドを追加するだけで行われます。しかしその後、オブジェクトのもっとも重要な機能である継承(inheritance)を導入することで、状況が込み入ってきます。

継承を使用しないうちは、オブジェクトはモジュール(modules)(単なるルーチンの寄せ集め)と大して違いはありません。あなたはオブジェクトを再利用するのと同じように、モジュールメソッドを再利用することができますが、その再利用の動作はいつも全く同じです。ところが、継承を用いると、オブジェクトは親からの主な機能を引き継ぐだけでなく、それ独自の機能を追加することができます。

さらに良いのは、オーバーライド(overriding)により、オブジェクトはその親の振る舞いを再定義することが可能です。こうして、2つのオブジェクトがdrawメソッドを持っても、それぞれ違うことを行います(サブオブジェクトは独自のdrawメソッドを定義し、親のメソッドをオーバーライドできます)。

これがOOPを便利にしているものです。我々は、再利用できるコードの利点がありながらも、カスタマイズできる機能も持っています。カスタム化しても、親オブジェクトの変更は一切ありません。さらに良いことに、どの外部コードも変更する必要がありません!

カプセル化

ここでは、その次に重要なオブジェクトの特徴であるカプセル化について見ていきましょう。カプセル化とは、オブジェクト(あるいはオブジェクトの集まり)を、外部のコードから分断して構築する方法です。これは、他のコードに影響しないでオブジェクトを修正し、オブジェクトに支障をきたすことなく外部コードを変更することができるので、きわめて便利で強力な方法です。これによりオブジェクトはより一般的で幅広いものとなります。

カプセル化とは、オブジェクトに独自のデータ仕様を取り入れることを意味する、ということを理解することが重要です(データを処理したり、保有しないオブジェクトは、役に立ちません)。外部コード(オブジェクトの一部でないコードのこと)は、オブジェクトのデータ構造がどのように構成されているのか何も分かりません。それは配列でしょうか?リンクされたリストでしょうか?または一連のプロパティでしょうか?これは誰にも分かりません!外部コードは、あらかじめ定義されたメソッドの設定を通じてのみ、オブジェクトと対話することができます。そこには、「addData」メソッド、「removeData」コマンド、そして「getData」関数などがあるでしょう。しかし、データがどのように構築され、保存されるのかはクラスが決めることです。

これを理解する一番よい方法は、カプセル化のアプローチと,カプセル化をしない方法を比べてみることです。

自由形式の住所録アプリケーションを作成したと仮定しましょう。それには名前、メールアドレス、電話番号、その他の連絡先があります。外観は次のようになっています:

インターフェースはシンプルです。下方のscrollbarは表示されるレコードを選択し、編集中のレコードを示しているstaticTextを更新します。上のボタンでは、レコードの追加、削除を行います。

もちろん、住所データはプログラムの操作上、データ構造を持って保存される必要があります。これは自由形式のデータベースなので、どのデータがどこにあるのかを気にする心配はありません。必要なことは、それぞれのデータ・レコードを別々にすることだけです。したがって、文字列の配列を使うことにしましょう。各々の文字列は、ひとつのデータ・レコードを含んでいます。

よって、ウィンドウには次のプロパティを追加します:

 theData(0) as string 

カプセル化をしないバージョンでは、ウィンドウのすべての機能は直接、データ構造(配列)を直接に処理します。よって、例えばnewRecordButtonは次のコードになります:

  
theData.append ""
scrollBar1.maximum = uBound(theData)
scrollBar1.value = scrollBar1.maximum

どのようにtheDataを直接に変更しているか分かるでしょう。これはプログラミングの悪い例です。データ構造の変更はできる限り少ない要素で行うことが望ましいのです。これはまた、scrollBar1を変更していて、将来別の問題を引き起こす可能性があります。

ScrollBar1コントロールのコードは次のようになります:

  
if me.value > 0 and me.value <= uBound(theData) then
// レコードに変更を保存
if oldValue > 0 and oldValue <= uBound(theData) then
theData(oldValue) = dataField.text
end if

dataField.text = theData(me.value)
staticText1.text = "Record " + str(me.value) + " of " + str(me.maximum)
oldValue = me.value
end if

さらに機能を追加していくと、コードはもっと複雑になっていきます。DeleteRecordButtonもまた、theDataに変更が必要となり、相互作用する多数の要素が作成され、各々のデータ構造を直接に操作します。

もちろん、今のところはこれで動きます…。問題は将来、あなたが機能(「Find」コマンドなど)をさらに追加しようとし、おそらくその時データ構造に手を加えようとしたときに起こるでしょう。そうすると、プログラム全体が破綻します――インターフェースとデータ構造間のすべてのコードを書き直さなければなりません。

詳細

「スパゲッティ」コードと言われるコードを聞いたことがあるでしょうか?例えるならば、絡み合ったクリスマスツリーのライトがいいでしょう――とてもゴチャゴチャしているでしょう!

しかし、それは複雑なプログラムに起こることとまったく同じです。要素Aは要素Bを、要素Bは要素Cを、要素Cは要素AとDを、そして要素Dは要素EとFを、またその2つが要素DとBを変更する――これはすぐに訳が分からなくなります。あなたがそのプログラムに精通している間は、追跡することができるでしょうが、新規のプログラマーだったり、一年間休んだ後にこのプログラムに着手したりすれば、コードの複雑さに戸惑うことでしょう。まだプログラムがどのように動くのか理解していなくても、プログラムの実行それぞれのステップで何が起こるのかを知り、プログラム実行の追跡はできなければいけません。

オブジェクトはスパゲッティコードをなくしてはくれませんが、正しくデザインすれば、回避することが易しくなります。カプセル化では、データ構造をあちこちのコードの部品の代わりに、わずかなルーチンのみで変更されるデータ構造にプログラムをデザインすることになります。それにより動作の追跡がはるかに容易になり、何が行われているのか分かります。

これをカプセル化を用いて記述すると、インターフェースのコードはデータオブジェクトにメッセージを送るだけなので、将来の更新がはるかに簡単になります。

ScrollBarコントロールのサブクラスを作成して、このアプリを書き直していきましょう。我々はそれをデータ・セントラル・コマンドとして、それだけにデータ構造の操作をさせます。ScrollBarsuperをもつscrollClassと呼ばれるclassを追加(ファイルメニュー、「新規クラス」)してください。

scrollClassクラスは、データ構造を操作するためのルーチン(メソッド)を含みます。そのデータ構造については、どの外部コードも知ることがなく、それを操作することもできません。

おわかりのように、我々はクラスに多くの項目を追加しました。我々はコントロールが開くときに実行される新規のイベント(initialize)を追加しました。それは、scrollClassが処理するeditFieldstaticTextオブジェクトへのリンクを設定するために必要になります。

ScrollBar1(いまはscrollClassオブジェクトです)の内部で、initializeイベントに次のコードを入力します:

  
// とても重要です!
// scrollbarを他の要素へリンクしなければなりません!
me.dField = dataField
me.label = staticText1

これによりオブジェクトは、window1の必要な要素とやりとりします。本当はプログラムのもっとも複雑な部分ですが、見ての通りそれほど複雑ではありません。それを使用する前に、正しくscrollBar1を初期化することを忘れずにしなければならないだけです。

一度初期化されれば、scrollBar1はすべてをコントロールします。window1のコードがどれほど簡単になったか見てください。いまでは、newRecordButtonactionイベントは次のようになっています:

  
scrollBar1.addRecord

DeleteRecordButtonは次のようになります:

  
scrollBar1.deleteRecord

外部コードがどれほど簡単になったか分かるでしょうか?いまでは、我々は好きなようにscrollClassを変更できますが、その外部コードを変更する必要はまったくありません。

言うまでもなくこのクラスを適切に終了するには、我々は読み出しやデータをファイルに保存するためのルーチンとfolderItemプロパティを追加する必要があるでしょう。それで、window1のファイルメニュー・コマンドは、ファイルを開いたり保存したりするためにたんにscrollClassへメッセージを送るだけになるでしょう。

この例によって、カプセル化の重要性が少し明らかになったことを期待します。ところで私はまだ、実際にはこのアドレスブックのアプリケーションを完了していませんが、お望みであればアプリのシェルを見ることができます。プロジェクトの両バージョン(カプセル化していないものとしているもの)を置いておきます。

次週

OOPの復習を終了し、理解すべき2、3の新しい概念に話題を変えます。

ニュース

先日、REAL Software社は、いつになるかと楽しみに待っていたニュースを発表しました!REALbasicは、マイクロソフト・ウィンドウズのプラットフォームで利用できるようになりました!標準製品は、99.99米ドルですが、プロフェッショナル版(通常399.99米ドル)は、2003年5月23日まで299.95米ドルの特別価格です。

アナウンスの全容は、ここで読むことができます。

REAL Software社はまた、free 5.1 updateをリリースしてMac製品もアップデートしました。このアップデートでは、問題を修正し、コンパイルの時間がとてもスピードアップしています――私はこの最新版をすぐにダウンロードすることを強くお薦めします!この最新版は、便利な「4.5→5.X」プロジェクト変換のPDFドキュメントが同梱されています。これは5.xがもたらすいくつかの変更点、新しいコンパイラーで古いプロジェクトが機能するにはどうするのかを詳しく述べています。

Letters

今週のお手紙は、Joe Riceさんが次のように書いてきてくれました:

はじめに、前回はアドバイス頂きありがとうございました。私の質問に、経験豊かなユーザが答えてくれるのは、素晴らしいことです。

さて、私の問題です。私はRBの全くの初心者で、プログラミングの計算部分の感触をつかむために、電卓をつくろうと思いました。開発は順調にいっていたのですが、小数点のある数を入力しようとしたときに問題が起こりました。

それは、integerは整数で、小数点以下の数をすべて無視してしまうので、テキストボックスからの文字列を、小数点をサポートするための数に変換するにはどうすればよいかと悩んでいました。

現在のところ、足し算のコードは次のようになっています:

  
ValueX=Val(XValue.text)
ValueY=Val(YValue.text)

Answer.text=Str(XValue+YValue)

大まかにはこうです。どうか私を助けるつもりで、これについて助言をください。

それでは!

Joe

私はいつになるかは分かりませんが、RBUのプロジェクトで電卓を取り上げることを実際に考えてしました。それは簡単ですが、たくさんの役立つテクニックを示すには良いプロジェクトです。

あなたの突き当たっている問題は、とても一般的なものです。あなたの犯している間違いは、doubleの代わりに、integerを用いていることです。あなたは、次のようにdoubleとして変数を定義しなければなりません:

  
dim ValueX, ValueY as double

doubleのデータ型は、有理数(小数点を持つ数)をサポートします。これは入力された数が小数点を含まなくても何の問題もありません。そして小数点があれば、数値はval()関数で正確に変換されます。これであなたのメインの問題は解決されたでしょう。

数値から文字列への逆の変換では、str()は小数点以下の数値を切り取ることがすぐに分かると思います。文字変換をうまく制御するには、代わりにformat()コマンドを使うことです。format()関数は、パーセント、通貨にも使え、大きい数値については自動的にコンマを追加してくれます。

これが役立つことを願います!


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

INDEXに戻る