序論
FreeBasic は、オブジェクト指向プログラミングを実装する方向に向かって、移行しています。
クラスは、まだ言語に追加されていませんが、Type 定義は、完全なクラスサポートに向かう第一歩として、いくつかのオブジェクト指向の構造物を含めるために、拡張されています。
この記事は、オブジェクト指向デザインの概念のいくつかを紹介して、拡張型構造物のいくつかについて説明します。
オブジェクト指向プログラミング
通常、OOP と短く呼ばれる オブジェクト指向プログラミングは、プログラマがオブジェクトと呼ばれるコード単位を組立てることを可能にする、方法論です。
オブジェクトは、プログラムの中で操られる必要がある、何かを表す、コードの単位です。
あなたは名詞としてオブジェクトを考えることができます:例えば、人、場所、もの。
オブジェクトは、例えば、基本図形のスプライトとか、ゲームの中に登場するタンクのように、より精巧な何か、かもしれません。
1セットの特性と機能を持っているどんな具体的な実体も、オブジェクトとして表すことができます。
オブジェクトは、オブジェクトが必要とするデータと、データに影響するメソッド(サブルーチンと関数)の、両方を含んでいます。
データとメソッドを、一つのエンティティ(単一体)に組分けることは、カプセル化、と呼ばれます。
あなたは、カプセル化で、複数のプログラムで再利用できるモジュラー・ユニットを作成できます。この、コード再利用の考えは、OOP の理論的枠組み作成の、主な動機でした。
カプセル化の別の有益な結果は、情報隠蔽です。
オブジェクトの中のデータは、データに求められていない変化が起こることができないように、外の世界から保護されます。
変数に直接アクセスする代わりに、オブジェクトには、外部プログラムが、データ・メンバーにアクセスして、変更するために使う、公開のインタフェースがあります。
インタフェースを使うことで、あなたは、オブジェクトがどう振る舞うかを制御できて、操作が多くのプログラムに対して一貫していることを、保証できます。
あなたは、インタフェースで、また、オブジェクトへのアクセス方法を変えずに、コード内の変更を行うことができます。
これは、既存の公開のメソッドを変えることです。あなたが、公開したインタフェースを変えない限り、オブジェクトを当てにするどんな既存のコードも変えずに、あなたはオブジェクトを改良できます。
プログラムで、改善したメソッドが必要なとき、あなたは互換性を維持するために、古いメソッドを残すことができます。そして、機能を向上させたと新しいメソッドを、加えるだけです。
新しいプログラムは新しいメソッドを使用できますし、古いプログラムは、まだ古いメソッドを使用できます。
公開のインタフェースを使う別の利点は、他のプログラマが、あなたのオブジェクトを、そのオブジェクト内の細部を心配せずに利用できることです。
公開されたインタフェースが、安定していて、説明が行き届いていれば、だれでもあなたのオブジェクトを利用できるでしょう。初心者でさえも。
公開された契約
前述のとおり、OOP は、プログラマの間で、コードを再利用できるようにするために、設計されました。
コード再利用が役立つように、公開されたインタフェースは、安定した状態を保たなければなりません。
すなわち、いったんオブジェクトが発行されて、プログラムで使われると、公開されたインタフェースは変更されないので、オブジェクトを使うプログラムは、正しく働き続けます。
オブジェクトの作者としてのあなたと、あなたのオブジェクトのエンドユーザとの間に、あなたは、オブジェクトに加える必要がある変更にかかわらず、公開したインタフェースを維持する、という、暗黙の契約があります。
作者とユーザの間の、この暗黙の契約は、OOP の理論的枠組みの主な強さであり、OOP がこのような強力なプログラミング方法論になった、主な理由です。
オブジェクトの特性
既に言及したように、オブジェクトは、データとメソッドの両方を含んでいます。
データは、オブジェクトの特性を説明します。メソッドは、オブジェクトができることを説明します。
簡単な、実際には役に立たない例で、この概念を説明します。
画面に長方形を描くオブジェクトを作成したい、と仮定してください。
長方形は、オブジェクトのデータ・メンバーの中に含まれている、数個の特性を持つことができます。
長方形は、画面の上に起点を持っています。通常、左上の隅です。x と y データ・メンバーは、起点からの距離です。
長方形には、幅と高さがあるので、オブジェクトは、幅と高さのデータ・メンバーを持つでしょう。
長方形は、外枠だけか、塗りつぶした状態があるので、塗りつぶしフラグのデータ・メンバーを、オブジェクトに加えることができます。
もちろん、長方形を描くとき、あなたは特定の色で描きたいことがあるでしょう。したがって、オブジェクトは、カラー・データ・メンバーが必要です。オブジェクトが、もう少しフレキシブルであれば、あなたは外枠のためのカラー・メンバーと、塗りつぶしのための別のカラー・メンバーを加えることができます。
もちろん、実際に画面に長方形を描くためのメソッドが必要なので、あなたは描画ルーチンをオブジェクト定義に加えることができます。
それで、私たちの長方形オブジェクトには、下の仮の特性とメソッドがあります:
- 特性:
起点 x と y
- 特性:
幅
- 特性:
高さ
- 特性:
塗りつぶし
- 特性:
外枠の色
- 特性:
塗りつぶしの色
- メソッド:
DrawRect
このリストは、オブジェクト定義と呼ばれます。
FreeBasic では、拡張 Type 定義を使ってオブジェクトを定義します。
拡張 Type は、標準 Type と同様です。拡張 Type は、いくつかの加えられた言語構築とともに、OOP の特徴の部分集合を実装します。
長方形の型定義
下の短いコードは、部分的な長方形定義です:
Type myRect
Private:
X_ As Integer
Y_ As Integer
Width_ As Integer
Height_ As Integer
Filled_ As Integer
Otlncolor_ As Integer
Fillcolor_ As Integer
Public:
Declare Sub DrawRect
()
End Type
お分かりのように、拡張 Type は、Private: と Public: キーワードと sub 宣言を除くと、標準 Type のように見えます。
Private: キーワードは、コンパイラに、これに続くデータ・メンバーは、型に対して、非公開である、と伝えます。これは、型の外からアクセスできません。
非公開の状態は、新しい限定子に遭遇するまで、すべてのオブジェクトのメンバーに適用されます。この場合、新しい限定子は、Sub 宣言の直前の Public: 限定子です。
すべてのデータ・メンバーは、外の世界に対して隠されていて、Type の範囲の外から変えることができません。情報隠蔽と呼ばれるプロセスです。
非公開変数名の後ろの下線文字は、非公開変数を定義する、一般的な方法です。
情報隠蔽は、オブジェクトの完全性を維持する方法です。
あなたは、外のプロセスを、データメンバーに、決して直接アクセスさせてはいけません。
すべてのデータは、Property メンバーを経由してアクセスさせるようにして、オブジェクトに渡されているものを制御できるようにします。
オブジェクトのデータを厳重に管理することで、プログラマがオブジェクトを使うときに発生する可能性がある、多くの誤りを、予防できます。
Type myRect
Private:
X_ As Integer
Y_ As Integer
Width_ As Integer
Height_ As Integer
Filled_ As Integer
Otlncolor_ As Integer
Fillcolor_ As Integer
Public:
Declare Sub DrawRect
()
Declare Property X
(ByVal xx_ As Integer)
Declare Property X() As Integer
Declare Property Y
(ByVal yy_ As Integer)
Declare Property Y() As Integer
Declare Property Width
(ByVal w_ As Integer)
Declare Property Width() As Integer
Declare Property Height
(ByVal h_ As Integer)
Declare Property Height() As Integer
Declare Property Filled
(ByVal f_ As Integer)
Declare Property Filled() As Integer
Declare Property Otlncolor
(ByVal oc_ As Integer)
Declare Property Otlncolor() As Integer
Declare Property FillColor
(ByVal fc_ As Integer)
Declare Property FillColor() As Integer
End Type
Public: 限定子に続く、Declare 命令文は、オブジェクトへの公開インタフェースを包含します。
型の変数は、Private: キーワードと共に定義されているので、変数にアクセスする唯一の方法は、オブジェクトの完全性を維持している、Property メンバーを通してだけです。
それぞれの Property メンバーの中で、コードを定義するので、あなたは、オブジェクトに入れられているものを、完全にコントロールできます。
この一般的な例は、特性のメンバーに、範囲検査コードを入れることです。オブジェクトが無効データを含まないようにするためです。
この例では、変数は、読み書き、できます。
コンパイラは、メソッドの型で、読み Property と、書き Property を見分けます。
サブルーチンでフォーマットされた Property は、非公開変数に保存された値を渡しているので、書き特性です。
関数でフォーマットされた Property は、非公開変数が呼び出し先に返されるので、読み特性です。
あなたは、まさしく関数でフォーマットされた Property を加えることによって、書き込み禁止 Property を作成できます。あるいは、ただサブルーチンでフォーマットされた Property を加えることで、書き込み Property を作成できます。
正常に動作するオブジェクトを作成する
定義は、ここで完全に見えますが、問題があります。
変数のいくつか、あるいは、すべてが初期化されないと、何が起こるでしょうか?
オブジェクトは、正しく実行しないで、潜在的に実行時エラーを引き起こしません。
一つまたは複数の変数が初期化されないときのために、オブジェクト変数のための、デフォルト値のセットを持っているほうがよいでしょう。
Constructor(構築子) を使って、オブジェクトを作成するときに、初期化できます。
Constructor(構築子) は、オブジェクトが、Dim(または、New)命令文を使って作られるときに呼ばれる、サブルーチンです。
構築子は、オブジェクトを、デフォルト値か、または、あなたが 構築子 に渡す値で、初期化することの役に立ちます。
これらを織り込んだ型定義は、下のようになるでしょう:
Type myRect
Private:
X_ As Integer
Y_ As Integer
Width_ As Integer
Height_ As Integer
Filled_ As Integer
Otlncolor_ As Integer
Fillcolor_ As Integer
Public:
Declare Sub DrawRect
()
Declare Property X
(ByVal xx_ As Integer)
Declare Property X() As Integer
Declare Property Y
(ByVal yy_ As Integer)
Declare Property Y() As Integer
Declare Property Width
(ByVal w_ As Integer)
Declare Property Width() As Integer
Declare Property Height
(ByVal h_ As Integer)
Declare Property Height() As Integer
Declare Property Filled
(ByVal f_ As Integer)
Declare Property Filled() As Integer
Declare Property Otlncolor
(ByVal oc_ As Integer)
Declare Property Otlncolor() As Integer
Declare Property FillColor
(ByVal fc_ As Integer)
Declare Property FillColor() As Integer
Declare Constructor
()
Declare Constructor(xx_ As Integer, yy_ As Integer, w_ As Integer, _
h_ As Integer, f_ As Integer, oc_ As Integer, _
fc_ As Integer)
End Type
あなたは、定義で、私たちが 2 つのConstructor(構築子) (一組のパラメータを持つもの、持っていないもの) を設定しているのに、気がつくでしょう。
これは、多重定義と呼ばれて、Constructor と共に使用されるだけではなく、他のサブルーチンや関数と共にも使用できます。
多重定義は、ただ一つのメソッド呼び出しで、異なったパラメータの型を扱う必要がある状況で、役に立ちます。
コンパイラは、メソッドに渡されるパラメータにもとづいて、どのメソッドを呼ぶか、を決定します。
あなたは、好きなだけ多く、メソッドを多重定義することができます。各メソッドのためのパラメタの数と型が、ユニークである限り。
この場合、Constructor が、何のパラメタ値も渡されないと、Constructor は、変数を、デフォルト値のセットで、初期化します。
Constructor が、パラメタ付きで呼ばれると、Constructor は、オブジェクトの変数を初期化するのに、渡された値を使います。
また、オブジェクトが破壊されるとき呼ばれる、Destructor メソッドがあります。
Destructor を使って、オブジェクトがメモリから解放される前に、行わなければならない全てのクリーンアップ・タスクを、実行できます。
オブジェクトが、何かポインタ参照を作成していたり、または何かファイルを開いていても、Destructor で、これらの参照を、消去できます。
Rectangle オブジェクトは、どんな外部の参照も作成しないので、Destructor は、必要ありません。
オブジェクト・メソッドを記入する
型定義は、オブジェクト型のためのテンプレートであり、メモリでオブジェクトをセットアップする方法を、コンパイラに伝えます。
しかし、オブジェクトを実際に使うために、あなたは、実際の呼び出しメソッドを、作成する必要があります。
下のリストを参照下さい。
Type myRect
Private:
X_ As Integer
Y_ As Integer
Width_ As Integer
Height_ As Integer
Filled_ As Integer
Otlncolor_ As Integer
Fillcolor_ As Integer
Public:
Declare Sub DrawRect
()
Declare Property X
(ByVal xx_ As Integer)
Declare Property X() As Integer
Declare Property Y
(ByVal yy_ As Integer)
Declare Property Y() As Integer
Declare Property Width
(ByVal w_ As Integer)
Declare Property Width() As Integer
Declare Property Height
(ByVal h_ As Integer)
Declare Property Height() As Integer
Declare Property Filled
(ByVal f_ As Integer)
Declare Property Filled() As Integer
Declare Property Otlncolor
(ByVal oc_ As Integer)
Declare Property Otlncolor() As Integer
Declare Property FillColor
(ByVal fc_ As Integer)
Declare Property FillColor() As Integer
Declare Constructor
()
Declare Constructor(xx_ As Integer, yy_ As Integer, w_ As Integer, _
h_ As Integer, f_ As Integer, oc_ As Integer, _
fc_ As Integer)
End Type
Sub myRect.DrawRect
()
Line (This.x_, This.y_)-(This.x_ + Width - 1, This.y_ + This.height_ - 1), This.Otlncolor_, B
If This.Filled_ <> 0 Then
Paint (This.x_ + 1, This.y_ + 1), This.Fillcolor_, This.Otlncolor_
End If
End Sub
Property myRect.x
(ByVal xx_ As Integer)
This.X_ = xx_
End Property
Property myRect.x() As Integer
Return This.X_
End Property
Property myRect.y
(ByVal yy_ As Integer)
This.Y_ = yy_
End Property
Property myRect.y() As Integer
Return This.y_
End Property
Property myRect.Width
(ByVal w_ As Integer)
This.Width_ = w_
End Property
Property myRect.Width() As Integer
Return This.Width_
End Property
Property myRect.Height
(ByVal h_ As Integer)
This.Height_ = h_
End Property
Property myRect.Height() As Integer
Return This.Height_
End Property
Property myRect.Filled
(ByVal f_ As Integer)
This.Filled_ = f_
End Property
Property myRect.Filled() As Integer
Return This.Filled_
End Property
Property myRect.Otlncolor
(ByVal oc_ As Integer)
This.Otlncolor_ = oc_
End Property
Property myRect.Otlncolor() As Integer
Return This.Otlncolor_
End Property
Property myRect.FillColor
(ByVal fc_ As Integer)
This.Fillcolor_ = fc_
End Property
Property myRect.FillColor() As Integer
Return This.Fillcolor_
End Property
Constructor myRect
This.X_ = 0
This.Y_ = 0
This.Width_ = 10
This.Height_ = 10
This.Filled_ = 0
This.Otlncolor_ = 15
This.Fillcolor_ = 7
End Constructor
Constructor MyRect (xx_ As Integer, yy_ As Integer, w_ As Integer, _
h_ As Integer, f_ As Integer, oc_ As Integer, _
fc_ As Integer)
This.X_ = xx_
This.Y_ = yy_
This.Width_ = w_
This.Height_ = h_
This.Filled_ = f_
This.Otlncolor_ = oc_
This.Fillcolor_ = fc_
End Constructor
メソッドとプロパティは、Sub/Function/Property
TypeName.methodname 構文を使って定義されます。
これは、メソッドを、適切な型定義と一致させる方法を、コンパイラーに指示します。
コンストラクタは、同じ理由で、型名で定義されます。
this 識別子は、定義された型を参照するメソッドに渡される、隠しパラメータです。
this 識別子を使って、型構成にアクセスすることを指定します。
あなたのオブジェクトを使う
オブジェクトは、今、完成しました。下にリストアップされているプログラムで、使えます。
Type myRect
Private:
X_ As Integer
Y_ As Integer
Width_ As Integer
Height_ As Integer
Filled_ As Integer
Otlncolor_ As Integer
Fillcolor_ As Integer
Public:
Declare Sub DrawRect
()
Declare Property X
(ByVal xx_ As Integer)
Declare Property X() As Integer
Declare Property Y
(ByVal yy_ As Integer)
Declare Property Y() As Integer
Declare Property Width
(ByVal w_ As Integer)
Declare Property Width() As Integer
Declare Property Height
(ByVal h_ As Integer)
Declare Property Height() As Integer
Declare Property Filled
(ByVal f_ As Integer)
Declare Property Filled() As Integer
Declare Property Otlncolor
(ByVal oc_ As Integer)
Declare Property Otlncolor() As Integer
Declare Property FillColor
(ByVal fc_ As Integer)
Declare Property FillColor() As Integer
Declare Constructor
()
Declare Constructor(xx_ As Integer, yy_ As Integer, w_ As Integer, _
h_ As Integer, f_ As Integer, oc_ As Integer, _
fc_ As Integer)
End Type
Sub myRect.DrawRect
()
Line (This.x_, This.y_)-(This.x_ + This.Width_ - 1, This.y_ + This.height_ - 1), This.Otlncolor_, B
If This.Filled_ <> 0 Then
Paint (This.x_ + 1, This.y_ + 1), This.Fillcolor_, This.Otlncolor_
End If
End Sub
Property myRect.x
(ByVal xx_ As Integer)
This.X_ = xx_
End Property
Property myRect.x() As Integer
Return This.X_
End Property
Property myRect.y
(ByVal yy_ As Integer)
This.Y_ = yy_
End Property
Property myRect.y() As Integer
Return This.y_
End Property
Property myRect.Width
(ByVal w_ As Integer)
This.Width_ = w_
End Property
Property myRect.Width() As Integer
Return This.Width_
End Property
Property myRect.Height
(ByVal h_ As Integer)
This.Height_ = h_
End Property
Property myRect.Height() As Integer
Return This.Height_
End Property
Property myRect.Filled
(ByVal f_ As Integer)
This.Filled_ = f_
End Property
Property myRect.Filled() As Integer
Return This.Filled_
End Property
Property myRect.Otlncolor
(ByVal oc_ As Integer)
This.Otlncolor_ = oc_
End Property
Property myRect.Otlncolor() As Integer
Return This.Otlncolor_
End Property
Property myRect.FillColor
(ByVal fc_ As Integer)
This.Fillcolor_ = fc_
End Property
Property myRect.FillColor() As Integer
Return This.Fillcolor_
End Property
Constructor myRect
This.X_ = 0
This.Y_ = 0
This.Width_ = 10
This.Height_ = 10
This.Filled_ = 0
This.Otlncolor_ = 15
This.Fillcolor_ = 7
End Constructor
Constructor MyRect (xx_ As Integer, yy_ As Integer, w_ As Integer, _
h_ As Integer, f_ As Integer, oc_ As Integer, _
fc_ As Integer)
This.X_ = xx_
This.Y_ = yy_
This.Width_ = w_
This.Height_ = h_
This.Filled_ = f_
This.Otlncolor_ = oc_
This.Fillcolor_ = fc_
End Constructor
'描画画面を作成します
Screen 18
'デフォルト 構築子 を使って、オブジェクトを作成します
Dim aRect As myRect
'明示的に構築子値を設定して、オブジェクトを作成します
Dim bRect As myRect = myRect
(200, 200, 200, 100, 1, 15, 9)
'長方形を画面に描きます
aRect.DrawRect
bRect.DrawRect
'aRect の特性を更新します
aRect.X = 90
aRect.Y = 20
aRect.Filled = 1
aRect.FillColor = 15
'新しい長方形を描きます
aRect.DrawRect
Sleep
End
デフォルト Constructor を使ってオブジェクトを初期化するには、ちょうど標準の型でするように、単に、拡張の Type を Dim するだけです。
Constructor が、ただ一つの値を取るだけなら、Dim var as Typename = value の構文を、使えます。
オブジェクトを、値のセットで初期化するには、型を Dim して、次に、= typename(par1m parm1 ...) 構文を使います。
あなたは、オブジェクトのメンバーにアクセスすることが、まるで、標準の型のメンバーにアクセスするようであることが、分かるでしょう。
cha0s さん、FreeBasicフォーラムでの、Properties に関連する情報を、ありがとうございました。