Object Oriented Programming with Free Pascal and Lazarus/ja

From Lazarus wiki
Jump to navigationJump to search

English (en) español (es) français (fr) magyar (hu) italiano (it) 日本語 (ja) македонски (mk) русский (ru) 中文(中国大陆) (zh_CN) 中文(臺灣) (zh_TW)

概要

Pascal に関する優れたチュートリアルは数多くあるが、このチュートリアルは初心者をさらに Turbo Pascal,、DelphiFree Pascal / Lazarusにより提供される標準 Pascalの拡張である、 オブジェクト指向プログラミングを深く理解することを目的としている。

オブジェクトは標準パスカルの構造のレコード構造の拡張である。

標準のテキストベースのPascalプログラミングは、1つのことを非常に巧みにこなす伝統的なUnixアプリケーションのようなアプリケーションを作るうえではよいものである。

プログラムが行う「1つのこと」は非常に複雑なアクションであるかもしれないし、ユーザーに複数のメニュー駆動型オプションを提供するかもしれない。しかし、本質的には、ユーザーがキーボードで入力するコマンドに従い、そのレスポンスを端末やプリンターに表示することに制限されている。

グラフィカルユーザーインタフェース (GUI)を提供するために、ある種のオブジェクト指向プログラミングメソッド(C言語やその派生、もしくはVisual Basic、例えば Lazarusを用いる、あるいは用いない Free PascalのようなPascalの派生 OO)が用いられるのは通常のことである。

GUIでは、ユーザーには以下のような様々な、道具やアクションに紐づけられた ウイジェットのセットからなる、整頓された多くの画像が示される。

  • メニューから選択する
  • ファイルを開く、保存する
  • インターネットに接続する
  • 算術演算を行う

など。

ユーザーは、画面上でマウスまたはその他のポインター/選択ツールを移動し、マウス ボタンのクリックまたはキーの押下に応じて実行されるアクションを選択することを期待している。

複雑なグラフィカル ユーザー インターフェイスを処理するシステムは、標準の Pascal またはその他のほぼすべてのプログラミング言語で作成できるものの、それぞれのグラフィカルオブジェクトが、画面上で、すべてのプロパティとプロシージャ、共通の構造とともに使用法が結びつけられた関数を持つことのできる、オブジェクト指向システムを用いるほうがはるかにたやすい。

オブジェクト - 現実世界の相似

病院や診療所で採取された血液サンプルの例を考えてみよう。

血液サンプル

物理的なサンプルは文字通り「実体」- objectであり、情報、ドキュメント、他の多くの物理的なオブジェクトが紐づけられている。

  • サンプルの入った試験管、の型は医師が望む試験の種類を規定する
  • ローカルルール (または メソッド, スタンダードオペレーティングプロシージャ) は看護師や技師にサンプルを集める指示を出す
    • どの種類の試験管が用いられるべきか
    • サンプルがどのように扱われるべきか
    • 検査室に運ばれるまでどのように保存されるべきか
  • ラベルは以下の細目について記載されている
    • サンプルの識別記号番号
    • 患者の氏名と誕生日
    • 採取した日付と時刻
    • 必要とする検査
  • 要望文書 それはサンプルとともに検査室へもたらされ、指示には
    • サンプルの識別記号番号
    • 指示した医師のID
    • 医師が望んだ検査
    • 患者のより詳しい情報
    • 確認が求められている、可能性がある診断

患者のカルテには、医師がしかるべき時に期待する結果を確認させる、要望文書のコピーが加えられる。

  • 検査室では - ローカルメソッドが以下を規定する
    • どのようにサンプルが分析されるか
    • どの機器を用いるか
    • どのようにその機器が補正され、運転されるべきか
    • どのように結果が保存され、形式が整えられるか
    • 医師に結果を報告すること

実際の 結果 が医師の診断を助けるために記録され、患者のカルテに検査結果のコピーが挟まれる。

物理的なサンプルは参照、確認、あるいはさらなる検査のために保存されるか、流しに捨てられる、あるいは焼却処分される。これを述べた方法があるだろう。

医師はサンプルが採取されるたびに、いちいちすべての細目と指示を書き込む必要はなく、検査室でそのサンプルがどのように検査されるかほとんど知らないだろう。この様々な課程の細目は、以前のサンプル採取と分析から継承され、全体の結果に対する、一定の計画、血液サンプルに対して考えらえる、ドキュメントとデータ、その基礎のメソッドを複合がオブジェクトである。 医師の考えでは、血液サンプルはその結果とほぼ同じ実体とみなされ、看護師や技師にとっては、サンプル、チューブ、ラベル、および保管状態が再び単一の実体を成す。

別の例 - 自動車

もし、血を見るのが嫌ならば、同様の理屈が車庫から、修理場へ運ばれる自動車にも適用できる。 それは以下よりなっているだろう:

  • 自動車自体
  • 所有者によって保存されている、登録証、車両ナンバー(車両ナンバープレートを含む)、保険証、購入の領収書、部品、修理などの書類
  • 燃料消費履歴
  • その自動車を使うことのできる運転者、およびその免許書
  • 修理場でのサービス記録
  • 定期点検とメンテナンスの方法(メソッド)と手順(プロシージャ)を追う書類
  • 非定期点検、修理を追うメソッド
  • 顧客の支払い情報

プログラミング例

こんな実社会の事例に没頭するのはもうたくさんだ! 主題のObject Pascalでのプログラミングに進もう。

Free Pascal/Lazarusでいくつかのコントロールを持つ簡単なフォームを作ることを考える。

ObjectInspector-TForm-ja.png
BlankForm-ja.png

Lazarus IDEを起動すると、プログラマにはフォーム設計の様々なコントロールや、オブジェクトを置くことを促す、空のテンプレートがもたらされる。

あらかじめ作られたフォームがすでにオブジェクトであるとこに注意すること。それは位置(上辺、左)、大きさ(高さ、幅)、色、テキストを加えるときのデフォルトのフォントなどのプロパティを持つ。


もしボタンコントロール(TButton型)がフォームに置かれると、それは、オブジェクトインスペクタで検討できるそれ自身の一連のプロパティを持つ。

そのプロパティのいくつかはフォームと似たような名前を持っている。これはある共通のその派生クラスによって、どのようにそのプロパティが定義され、扱われるかをレイアウトする祖先クラスから継承されたプロパティであることが理由である。

プロパティ同様に、オブジェクトインスペクタはアプリケーションがどのように、例えばマウスボタンクリック(OnClick)もしくは位置や、大きさ、他のプロパティを変更(OnChange)したときに、それらを扱うかを指示するメソッドであるイベントハンドラにアクセスするイベント(Events)という名前のタブを持つ。

フォーム上の物理的な画像であるボタンは、そのプロパティとイベントハンドラと相まって、パスカルでは単一の実体オブジェクト(Object)とみなされる。

ObjectInspector-TButton-ja.png
FormWithButton-ja.png
Source FormWithButton1-ja.png

標準パスカルに対するオブジェクト指向拡張

Pascalのレコード構造はオブジェクトを定義することで拡張された。

オブジェクト

オブジェクトは特別な種類のレコードである。 レコードには、次のすべてのフィールドが含まれる。 (従来のレコードと同様に) オブジェクトの定義で宣言されるが、プロシージャと関数はレコードの一部であるかのように宣言でき、オブジェクトの型に関連付けられたメソッドへのポインタとして保持されるようになった。

例えば、平均を計算するメソッドともに実数値の配列を保持するオブジェクトを仮定する。

Type
  Average = Object
    NumVal: Integer;
    Values: Array [1..200] of Real;
    Function Mean: Real; { 配列の平均値を計算する }
  End;

「オブジェクト」はフィールドとメソッドを「親」オブジェクトから「継承」することができる。これはこれらのフィールとメソッドが、「子」として宣言されたオブジェクトに含まれているかのように、用いることができることを意味する。

さらに、視認性の観念は、フィールド、プロシージャ、関数がpublic、protected、privateとして宣言することを可能とした。デフォルトではフィールドとメソッドはpublicで、現在のユニットの外側へエクスポートできる。Protectされたフィールドやメソッドは現在の祖先オブジェクトから派生したオブジェクトにとってのみ利用可能である。Privateで宣言されたフィールドや、メソッドは現在のユニットでのみアクセスが可能である:そのスコープは現在のユニットの実装に限られている。

クラス

オブジェクトはFree PascalとLazarusではそう頻繁には用いられない。むしろ、クラス(Class)は広く用いられる。クラスはほとんどオブジェクトと同様に宣言されるが、オブジェクト自身というよりはオブジェクトに対するポインタである。技術的には、これはプログラムのヒープに割り当てられるが、オブジェクトはスタックに割り当てられる。

これはクラス宣言の典型的な例である:

{-------------------------------------------}
{        LCLからのクラス宣言の例            }
{-------------------------------------------}
  TPen = class(TFPCustomPen)
  private
    FColor: TColor;
    FPenHandleCached: boolean;
    FReference: TWSPenReference;
    procedure FreeReference;
    function GetHandle: HPEN;
    function GetReference: TWSPenReference;
    procedure ReferenceNeeded;
    procedure SetHandle(const Value: HPEN);
  protected
    procedure DoAllocateResources; override;
    procedure DoDeAllocateResources; override;
    procedure DoCopyProps(From: TFPCanvasHelper); override;
    procedure SetColor
         (const NewColor: TColor; const NewFPColor: TFPColor); virtual;
    procedure SetFPColor(const AValue: TFPColor); override;
    procedure SetColor(Value: TColor);
    procedure SetMode(Value: TPenMode); override;
    procedure SetStyle(Value: TPenStyle); override;
    procedure SetWidth(value: Integer); override;
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    property Handle: HPEN read GetHandle write SetHandle; deprecated;
    property Reference: TWSPenReference read GetReference;
  published
    property Color: TColor read FColor write SetColor default clBlack;
    property Mode default pmCopy;
    property Style default psSolid;
    property Width default 1;
  end;

このクラスは他のparent(親)もしくはancestor(祖先)クラス(TFPCustomPen)のインスタンスとして宣言されていることに注意すること。その祖先から、すべてのプロパティとメソッドをinherit(継承)している。このクラスはそれ自身のフィールドを有しており、以下にグループ化されている。

  • private - これはここで定義されたアイテムが、同じプログラムユニットの他のクラスもしくはプロシージャ/関数からのみ用いるあるいは、見ることができる(この例では、Graphicsから、そのように他のクラスのいずれ、例えば、同じユニットのTBitMapTPictureなどでもそれらを使うことができる)ことを意味する。それらは本質的にはローカル変数(例えばFColorFPenHandleCached)もしくは、ローカルで用いられるメソッド(GetHandleSetHandle)だが、protectedもしくはpublic節で宣言されたアイテムで用いる、あるいは参照されることができる。
  • protected - これはここで定義されたアイテムが、この祖先クラスから派生したクラスにとってのみ用いる、あるいは見ることができることを意味し、そのプロパティとメソッドを継承する。
  • public - これはここで定義されたアイテムが Uses句の現在のユニットを含むどのプログラミングユニットからも利用できることを意味する。
  • published - はpublic節と同様であるが、コンパイラが、これらのクラスの自動的ストリーミングにとって必要な型情報も生成する。パブリッシュされたアイテムはLazarusのオブジェクトインスペクタに通常現れる。もし、パブリッシュされたリストがない場合、オブジェクトインスペクタには、いつもすべてのpublicフィールドが現れる。

メソッド

メソッドは標準のプロシージャ、もしくは関数に似ているが、いくつかの指令(directive)を持つ。 上記で定義されているメソッドのいくつかは指令、virtual、他はoverride指令が付けられている。

  • virtual はこのメソッドの型、もしくは実際のインスタンスがコンパイル時に知られていないことを意味するが、何のサブプログラムが実際にこのメソッドを呼ぶかによって、実行時に選択される。これはクラスの定義において場所取りとして、考えることができる。
  • overrideは実行時のローカルな定義が、特にもしそれが実質的(virtual)であるならば、派生クラスから継承された定義の場所をとってかわることを意味する。もし、特に、祖先クラスで定義されたメソッドを使いたいときは、特異的に継承された句とともに呼ぶ必要がある。

Virtualあるいはoverride指令のないメソッドは静的(static)メソッド(Pascalにおける通常の種類)である。Virtualあるいはoverride指令は動的(dynamic)である。

メソッドの特定のインスタンスは:

  • create - メモリの割り当て、全ての必要かつ、様々なプロパティを設定/初期化するためにな情報を一緒に集める、コンストラクタ(constructor)である。
  • destroy - 順に従い、論理的にシステムからクラスのすべての部品を取り除く、そして再使用するためシステムにそのリソースを返すディストラクタ(distructor)である。

プロパティ

プロパティは従来的なPascalにおける通常のフィールドに似ているが、読み取り(read)あるいは/および書き込み(write)指示子を持つ。

  • read指示子はプロパティの正しい結果を返すフィールド、あるいは関数である。上記の例では、プロパティColorは使われる値を保持しているローカル変数である、読み取り指示子FColorを持っている。もしプロパティがreadを持ち、write指示子を持っていないなら、それは読み取り専用(read-only)である。
  • write指示子は特定の場所の値を保持する、フィールドもしくはプロシージャである。上の例ではColorは特定の場所に対する色を書き込むためのプロシージャ(protected節で宣言されている)である書き込み指示子、SetColorを持っている。もしプロパティがwriteを持ち、readを持たないなら、書き込み専用(write-only)である。
  • default - default値をプロパティに持たせることが可能なことに注意すること。例えば、ここでColorが生成時にデフォルト値clBlack、もしくは黒を持っている。それは引き続き異なった値が、プログラムの割り当てまたは、オブジェクトインスペクタにより、与えられうる。
  • index指示子は、プロパティ間で共有される、read もしくは writeメソッドが、どのプロパティが望ましいか特定するために用いることのできる整数値である。もし、indexread もしくは writeで用いられているなら、指示子はそれぞれ、関数、もしくはプロシージャなければならず、通常のフィールド/値たりえないことに注意すること。


  TFooClass = class
  private
    FIntProp: Integer;
  public
    property IntProp: Integer read FIntProp write FIntProp;
  end;
  TFooClass = class  
  private
    function GetListProp(AIndex: Integer): String;
    procedure SetListProp(AIndex: Integer; AValue: String);
  public
    // プロパティのこの型はクラスのpublishedセクションにはないだろう
    property ListProp[AIndex: Integer]: String read GetListProp write SetListProp; 
  end;
  TFooClass = class
  private
   function GetValue(const AIndex: Integer): Integer;
   procedure SetValue(const AIndex: Integer; AValue: Integer);
  public
    // note that the read and write methods are shared
    property Value1: Integer index 1 read GetValue write SetValue;
    property Value2: Integer index 2 read GetValue write SetValue;
    property Value3: Integer index 3 read GetValue write SetValue;
    property Value4: Integer index 4 read GetValue write SetValue;
    // インデクスは定数か、数値、あるいは型を列挙することもできる
    // 例えば:
    // property Value: Integer index ord(seSomeEnum) read SomeFunction write SomeProcedure;
  end;

Free Pascal 言語拡張

FPCはオブジェクト指向プログラミングをサポートするためにその「標準」Pascal文法にいくつかの言語拡張を含んでいる。

これらの拡張はFPC Language Reference Guide http://www.freepascal.org/docs.var.(訳注: このリンクは無効であるFPCのリファレンスに対するURLは https://www.freepascal.org/docs-html/current/ref/ref.html である)で述べられており、それぞれの概念に対するチュートリアルへのリンクもある。Language Reference Guideはこの概要チュートリアルには含まれない文法便覧とさらなる細部を含んでいる。上記には4つの言語仕様が含まれており、ObjectsClassesはFPCとLazarusにおける、オブジェクト指向プログラミング (OOP)の基礎を成している。OOP初心者にとって、Objectsセクションはより概説的なセクションで、ClassesセクションはObjects文法に対する類似性と相違性を強調することで、反復を最小限にしている。一般的に Classes 実装はDelphi、Lazarus開発者により広く用いられている。FPCではOOPの Classes 方言で、何が実際「class」なのかに言及することに「object」がよく使われている。これらのドキュメントは命名上の混乱を最小限にするために記述されているが、このドキュメント以外では、通常、 Class から作られるオブジェクトに用語「object」が用いられている。実際、FPCランタイムライブラリ(RTL)は TObject と呼ばれる基底 class を持つクラスライブラリを含んでいる。

古いTurbo Pascal OOP実装に慣れているユーザーはオブジェクト実装が古いTurbo Pascal方言に基づいているため、Classesセクションを初めは飛ばしたいかもしれない。ClassesセクションはDelphi文法に基づいているため、Delphiユーザーにはなじみがあるはずだ。Classesセクションの書き方はObjectsセクションからの概念を参照しているかもしれないことに注意すること。様々なApple、THINK、MPW Object Pascal方言になじみのあるマッキントッシュ開発者にとって、FPC ObjectsもしくはClasses方言のいずれも直接的な移行パスとならないだろう。2009年 3月時点では、Mac PascalメーリングリストではAppleのObjective C / Cocoa フレームワークにアクセスするためにあるコンパイラサポート(新しい文法)を提供する可能性が議論されている。

オブジェクト指向 Pascal 一般的な概念

OOPは利用可能な他の言語仕様と設計に比べ、データの取り扱いとカプセル化とプログラムフローを取り扱う異なる方法を提供する。OOP は多くの場合、グラフィック ユーザー インターフェイス (GUI) や物理システムなどの特定のアプリケーションを、より自然な感覚でモデル化するのに役立つ。しかし、OOPはすべてのアプリケーションに適切ではない。プログラムコントロールはより基本的なPascal手続き的構造ほど明示的ではない。OOPから最も恩恵を受けるためには、学習曲線が急であることを通常必要とする、大きなクラスライブラリの理解が必須とされる。厳格な手続き的コードをメンテナンスすることに比べ、大規模なOOPアプリケーションコードをメンテナンスすることは、その利点と、欠点がある。OO分析、設計とプログラミング技法に対する豊富な学習教材があるが、それらは本稿の範疇を超えてしまう。

拡張、あるいはその言語の基本として、OOPフィーチャを取り込んだ多くのプログラミング言語が存在する。このように、OOコンセプトを記述するために多くの異なる用語がある。FPCですら、いくつかの命名上の重複がある。一般的に、OOPはデータ上で機能するデータとプロシージャの関連したセットを明示的に組み合わせ、カプセル化するプログラミングオブジェクト(もしくは、情報ユニット)の概念よりなっている。データは通常、プログラムの実行中に永続的だが、グローバル変数の宣言に固有の問題を軽減するメカニズムが備わっている。さらに、OOP 言語を使用すると、以前に定義されたオブジェクトに基づいてオブジェクトを段階的に変更および/または拡張できる。 この機能は通常、継承およびポリモーフィズムという用語で呼ばれる。多くのOOP言語は、1つのオブジェクトに属する、プロシージャを称するのに、メソッド もしくは メッセージ用語を用いる。OOPの力の源泉はコンパイルバインディングよりも、メソッドの遅い(実行時)、動的なバインディングによって、認識される。このメソッドの動的なバインディングは、プロシージャ変数とプロシージャ パラメータの使用に似ているが、構文の凝集性が高く、関連するデータとのカプセル化が行われ、以前に定義されたメソッドの動作も継承される。次の Wiki ページは、オブジェクト指向の方法での分析、設計、プログラミングについてさらに詳しく知るための出発点を提供する。

さらなる情報

これは、この話題についてほんの触りを述べたに過ぎない。より詳細には、Free Pascalマニュアルの特に、5章(Objects)と6章(Classes)を読むことを強く推奨する。

ボーランドはまた、寛大にもPDF媒体で、オリジナルのTurbo Pascalマニュアルから、オブジェクト指向プログラミングの章を解放していた。この章ではこの言語仕様をボーランドがTurbo Pascalに導入したときからが書かれている。これは古いかもしれないが、OOPの理論は今日でも非常に関連性がある。Linkの「その他」のカテゴリから、ダウンロードできる。

以下も参照のこと

外部リンク