Difference between revisions of "Object Oriented Programming with Free Pascal and Lazarus/zh TW"

From Lazarus wiki
Jump to navigationJump to search
Line 172: Line 172:
 
*'''發佈''' - 這和公開'''public'''區的是一樣的,但編譯器同時會產生類型的資訊給自動串流的物件用。屬於'''發佈'''的欄位會列在物件檢視器上,若這裡沒有發佈的欄位,物件檢視器會以'''公開'''的欄位取代之。
 
*'''發佈''' - 這和公開'''public'''區的是一樣的,但編譯器同時會產生類型的資訊給自動串流的物件用。屬於'''發佈'''的欄位會列在物件檢視器上,若這裡沒有發佈的欄位,物件檢視器會以'''公開'''的欄位取代之。
  
=== Methods ===
+
=== 方法 ===
A method is just like a standard procedure or function, but can have some additional '''directives'''.
+
方法其實就像函式或程序,但它可以額外多一個'''指令''' (directives) 的功能.
  
Some of the methods defined above are labelled with the directive '''virtual'''; others are labelled with the '''override''' directive.
+
有些方法在宣告的時候下面就會標記著 '''virtual''' (虛擬)這個指令;或是某些標著'''override'''(覆蓋)這個指令。
*'''virtual''' means that the type or actual instance of a method is not known at compile-time, but is selected at run-time depending on what sub-program actually calls the method. It could be considered a place-holder in the definition of the class.
 
*'''override''' means that at run-time the locally given definition can take the place of a definition inherited from an ancestor class, particularly if it was '''virtual'''. If you particularly want to use the method defined in the ancestor class, it is sometimes necessary to call it specifically with the '''inherited''' clause.
 
  
Methods with no virtual or override directive are '''static''' methods (the usual kind in Pascal). Those with a virtual or override directive are '''dynamic'''.
+
*'''virtual''' 代表這個副本的方法在編譯期還是未知的,但是等到程式的執行期如果該子程式被呼叫才會被選出來,也就是在定義的時候,這個宣告可以為此方法在類別預留空間。
 +
*'''override''' 這代表程式在執行期它從祖宗類別繼承下來的方法可以就地取代,尤其是對虛擬的方法。如果你想特別針對繼承下來的方法去使用,則你在呼叫的時候就需要用到'''inherited'''(繼承)這指令。
  
Special instances of methods are:
+
方法若即不下虛擬或覆蓋的指令,則它們屬於 '''static'''(靜態)的方法 (也是 Pascal 最一般的情況)。前兩者指令都算是'''動態'''的。
*'''create''' - a '''constructor''' for a class, which takes care of allocating memory, collecting together all the information needed and configuring/initializing the various properties.
+
 
*'''destroy''' - a '''destructor''' for a class, which removes all the parts of the class from the system in an orderly and logical way, and returns all its resources for the system to re-use.
+
方法的特殊例子:
 +
*'''建立''' - 類別的'''創建子''',用來負責記憶體的分配,收集所有所需要的資訊來設計/初始化所有類別裡的內容。
 +
*'''銷毀''' - 類別的'''解構子''',有條理地將類別從系統中移掉,並將它本來佔有的資料還給系統做重新利用。
  
 
=== Properties ===
 
=== Properties ===

Revision as of 10:45, 25 December 2010

Template:Object Oriented Programming with FreePascal and Lazarus

使用 Free Pascal 與 Lazarus 寫作物件導向程式

介紹

Pascal 有很多很好的教材,但這篇教學主要是讓初學者能更往前進到物件導向的程式設計,物件導向算是 Pascal 的延伸,從 Turbo-Pascal 提出,一直到 Delphpi,Free Pascal/Lazarus。

物件乃是標準 Pascal 的延伸 記錄結構。

文字基礎的 Pascal 程式撰寫能夠在像 Unix 下做成良好的應用程式,穩定地進行單一的工作。唯一一件設計師會把他寫的很複雜的事情,就像是需要做選項趨向的選單,但這基本上對於使用者想要下命令讓電腦,或印表機去照著做的空間還是很有限。

為了要提供 圖形化的使用者介面 (GUI),它通常都得必須和物件導向的程式設計方法脫離不了關係 (常常像是使用 C 或是他的延伸產品,或 Visual Basic,或其 物作導向衍生產品,也就等於Pascal 和 Free Pascal 相對於 Lazarus)。

在 GUI 裡使用者看到有一大堆的圖形有組織地排列在畫面上,分成數個工具群組分類放置,每種工具都可以做一些相關的動作像

  • 從選單選取項目,
  • 開啟或儲存檔案,
  • 連線到網路,
  • 進行數字運算,等等。

使用者可以移動滑鼠或其他指標裝置來選取畫面上的工具,利用點擊滑鼠左右鍵或按下鍵盤按鍵來執行一些動作。

當系統想要達成這樣的圖形使用者介面時,想要用標準的 Pascal 或是其他程式語言來撰寫,若導入物件導向語法就會變的很簡單,如所見的畫面上的每一個工具在程式裡都寫成一個物件,在共通統一的結構下擁有與自己相關的內容屬性,函式與程序。

物件 - 現實世界的例子

假想你在醫院或一個醫生的辦公室裡,想要分析收集來的血液樣本。

血液樣本

這些樣本的實體就算是一個物件,它有很多的訊息包含著或有其他的文件和物件與其有關。

  • 試管,即內科醫師用來進行檢驗的地方。
  • 內部流程 (或稱方法標準操作流程 (SOP)) 來指引護士或技師來採樣
    • 要用哪一種試管,
    • 如何進行採樣
    • 如何儲存樣本並送回實驗室。
  • 試管上的標籤訊息代表著
    • 採樣編號
    • 患者的姓名與生日
    • 採樣日期
    • 是否需要檢驗。
  • 在實驗室裡檢驗請求單會伴隨著樣本作業,指出
    • 採樣編號
    • 請求檢驗的醫師編號
    • 請求檢驗的醫師所要求的檢驗內容
    • 患者完整的資訊
    • 所希望看到的診斷確認。

請求單副本將放入患者的病歷表裡,去提醒師生在適當的時間內做出推斷出結果。

  • 在實驗室中 - 內部方法決定了
    • 樣本要如何來分析
    • 要用什麼樣的機械
    • 機械要如何去校正與操作
    • 結果要用什麼樣的格式寫下來,然後
    • 回報給醫生。

實際上結果即是醫生診斷後的記錄,然後一份副本留存在患者的病歷中。

樣本的實體也許還會留下來做參考用,以供未來確認或進一步再檢驗。或是通通棄置焚化掉;這也會有一些方法來專門處理這件事。

這個流程中醫師不需要在每一次要進行採樣時都要把詳細在重頭重述一遍;而且實際上醫師很可能只懂自己在實驗室裡要進行檢驗這部份的知識而已。而其他各種流程裡的智識是從之前的採樣與分析裡繼承下來的 - 這個流程會有一個通用旳操作準則,我們共稱做血液採樣,所有在這個方法之下它的文件與資料,用來組成這個物件

在醫師的心裡,血液採樣和它的結果被視為是同一件事(實體;entity),而對技師與護士而言,有關於試管,標籤和儲存方法,也都成為單獨的一件事(實體)。

另一個例子 - 汽車

如果你不喜歡血,同樣的我們可以舉另一個汽車拉進修車廠做修理的例子來說明。 修理過程可能包含:

  • 汽車實體
  • 擁有者所有的文件:任何證件 (包括車牌),保險,購買發票,配件,修理部位等
  • 油料耗用情形
  • 駕駛者的行照
  • 該修車廠的服務記錄
  • 整個維護與檢查行程的方法與程序
  • 例行行程之外的修理方法等
  • 客戶的付帳方法與資訊

程式範例

我想現實世界的舉例已經足夠!讓我們進入到我們真正的目的,撰寫物件化的 Pascal。

讓我們透過 Free Pascal/Lazarus 來建立一個有簡單的表單,帶幾個控制項的應用程式,說明以下狀況。

ObjectInspector-TForm.png
BlankForm.png

鑑於 Lazarus IDE,程式設計師從一份空白的表單樣版開始進行他的設計,想像設計師大膽地在表單上放入了很多的控制項與物件。

首先注意那張空白的表單已經是一個物件了,有它自己的內容,像是位置 (上基準點,左基準點),尺寸 (高與寬),顏色,加入文字時的預設字型等等。



假設一個按鈕控制項加入到表單裡 (type TButton),它也會有自己一系列的內容,可以在物件檢視器視窗中觀察到。

有幾個內容是跟空白表單是一樣的;這是因為很多內容都是繼承自上一個祖宗類別,它們已經原本就定義好它的子嗣物件要如何去處理這些內容。

同等於內容,物件檢視器裡還有一個頁籤叫事件,可以用來安排在事件處理事如像滑鼠鍵按一下按鈕 (OnClick) 或是像尺寸,位置等有了改變 (OnChange) 的時候,應用程式是要用來哪些方法或程序來進行。

在表單上那個按鈕實際的影像,就應該被認為是和他所有的內容,事件是同一體的,在 Pascal 下可稱為實體 (Entity) 或物件 (Object)。

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

標準 Pascal 物件導向的擴充

Pascal 記錄結構透過以下定義來擴充

物件

物作算是一種特別的記錄。記錄的內容包括所有物件定義宣告的欄位(就像是所有一般的記錄),但現在程序和函式也可以被視為記錄的一部份,利用指標 (pointer) 指到原有物件型態下的方法來宣告。

舉例說明,一個物件可以包含有一個陣列,裡面帶有實際的值,然後透過方法來計算出它的平均。

Type
  Average = Object
    NumVal: Integer;
    Values: Array [1..200] of Real;
    Function Mean: Real; { 計算陣列的平均值 }
  End;

物件可以從「父」物件「繼承」欄位內與方法。這表示著,如果物件在宣告時加入是某某的「子」物件,也就可以使用同樣的欄位與方法。

更甚者,這裡還要導入一個可見度的概念:欄位,程序與函式都可以宣告成公開 (Public),保護 (Protected) 或私有 (Private) 三種,預設狀況欄位與方法都是公開的,可以匯出到外部。保護的欄位和方法在物件進行繼承的時候會從目前的祖宗物件傳給子嗣物件。而宣告成私有的欄位或方法,就只有目前自己的單元下可以存取:也就是它們的視界 (Scope) 被侷限於目前單元的實作裡面。

類別

Free Pascal 與 Lazarus 裡,物件並不是最常被用到那個部份;取而代之的類別才是廣泛的在使用,類別在宣告的步驟上和物件是相同的,但是它其實是個指標,指到物件,而不是物件本身。技術上層面來講,類別在程式裡的記憶體分派方式是堆積 (Heap;手動分配),而物件是用堆疊 (Stack;自動分配;空間有限)來分配。

這裡有一個簡單的典型類別宣告範例: <delphi>{-------------------------------------------} { 從 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;</delphi>


請注意這個類別被宣告是另一個或稱祖宗類別 (TFPCustomPen) 的副本,也就是會繼承它所有的內容與方法。同時它也有屬於自己私有的欄位,被歸類於

  • 私有 - 在這裡定義的項目就代表它們只能被在同一個程式單元的其他類別或程序/函式所看到,利用 (有個例子是 Graphics,所以像其他的類別如 TBitMapTPicture 等等存在同一支單元就可以使用它)。它們基本上都算當地的變數 (如 FColorFPenHandleCached) 或就地使用的方法 (GetHandleSetHandle),但當變數保護公開區段宣告的也一樣可以被使用。
  • 保護 - 這裡的項目代表它們只能被繼承這個祖宗類別裡內容與方法的子嗣們所看到
  • 公開 - 這裡定義的項目代表他們在所有程式單元裡都可以看到,只要他在程式 Uses 語法的區段有 include 進來。
  • 發佈 - 這和公開public區的是一樣的,但編譯器同時會產生類型的資訊給自動串流的物件用。屬於發佈的欄位會列在物件檢視器上,若這裡沒有發佈的欄位,物件檢視器會以公開的欄位取代之。

方法

方法其實就像函式或程序,但它可以額外多一個指令 (directives) 的功能.

有些方法在宣告的時候下面就會標記著 virtual (虛擬)這個指令;或是某些標著override(覆蓋)這個指令。

  • virtual 代表這個副本的方法在編譯期還是未知的,但是等到程式的執行期如果該子程式被呼叫才會被選出來,也就是在定義的時候,這個宣告可以為此方法在類別預留空間。
  • override 這代表程式在執行期它從祖宗類別繼承下來的方法可以就地取代,尤其是對虛擬的方法。如果你想特別針對繼承下來的方法去使用,則你在呼叫的時候就需要用到inherited(繼承)這指令。

方法若即不下虛擬或覆蓋的指令,則它們屬於 static(靜態)的方法 (也是 Pascal 最一般的情況)。前兩者指令都算是動態的。

方法的特殊例子:

  • 建立 - 類別的創建子,用來負責記憶體的分配,收集所有所需要的資訊來設計/初始化所有類別裡的內容。
  • 銷毀 - 類別的解構子,有條理地將類別從系統中移掉,並將它本來佔有的資料還給系統做重新利用。

Properties

Properties are just like ordinary fields in a conventional Pascal record, but they can have read and/or write specifiers.

  • read specifier is a field, or a function that returns a result of the correct type for the property. In the example above, the property Color has a read specifier FColor, which is a local variable containing the value to be used. If a property has a read but no write specifier, it is read-only.
  • write specifier is a field, or a procedure that will store the value of the property in a specific location. In the example above, Color has a write specifier SetColor that is a procedure (defined in the protected section) for writing the color value to some specified location. If a property has a write but no read specifier, it is write-only.
  • default - note that it is possible to set a default value for a property. For example, Color here is given the default value clBlack, or black, at the time of creation. It could subsequently be given a different value, by a programming assignment statement, or in the Object Inspector.
  • index specifier is an integer that the read or write methods, shared between properties, can use to identify which property is desired. Note that if index is used the read or write specifiers must be a function or a procedure respectively and cannot be a normal field/variable.

Examples

<delphi>

 TFooClass = class
 private
   FIntProp: Integer;
 public
   property IntProp: Integer read FIntProp write FIntProp;
 end;

</delphi>

<delphi>

 TFooClass = class  
 private
   function GetListProp(AIndex: Integer): String;
   property SetListProp(AIndex: Integer; AValue: String);
 public
   // this type of property may not be in the published section of a class
   property ListProp[AIndex: Integer]: String read GetListProp write SetListProp; 
 end;

</delphi>

<delphi>

 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;
   // index may be a const or a number or you can even use an enumerated type
   // for example:
   // property Value: Integer index ord(seSomeEnum) read SomeFunction write SomeProcedure;
 end;

</delphi>

Free Pascal Language Extensions

FPC includes several language extensions to its "standard" Pascal syntax to support object oriented programming.

These extensions are described in the indicated chapters of the FPC Language Reference Guide: http://www.freepascal.org/docs.var. Links to tutorial pages for each concept are included above as well. The Language Reference Guide includes syntax diagrams and further details not contained in this introductory tutorial. Of the four language features listed above, Objects and Classes form the basis of object oriented programming (OOP) in FPC and Lazarus. For those new to OOP, the Objects section includes more introductory concepts and the Classes section minimizes repetition by emphasizing the similarities and differences to the Objects syntax. In general, the Classes implementation seems to be more widely in use including Delphi Lazarus developers. Often the word "objects" is used to refer to what is actually a "class" in the Classes dialect of OOP in FPC. These documents will be worded to minimize any terminology confusion, but outside of this document, the term "object" oftentimes refers to objects created from a Class. In fact, the FPC run time library (RTL) includes a class library with a base class called TObject.

Users familiar with the older Turbo Pascal OOP implementation may initially want to skip the section on Classes since the Objects implementation is based on the older Turbo Pascal dialect. The section on Classes should be familiar to Delphi users since it is based on Delphi syntax. Be aware that some of the writeup in the Classes section may refer to concepts from the Objects section. For Macintosh developers familiar with the various Apple, THINK and MPW Object Pascal dialects, neither the FPC Objects or Classes dialects provide a direct migration path. As of March 2009, there are discussions on the Mac Pascal Mailing list about potentially providing some compiler support (new syntax) for accessing Apple's Objective C / Cocoa framework.

General Concepts of Object Oriented Pascal

OOP provides different ways to manage and encapsulate data and to manage program flow compared with other available programming language features and constructs. OOP often lends itself to modeling certain applications such as Graphic User Interfaces (GUI's) and physical systems in a more natural feeling manner. However OOP is not appropriate for all applications. Program control is not as explicit as the more basic Pascal procedural constructs. To obtain the most benefit from OOP, understanding of large class libraries is often required which can entail a steep learning curve. Maintaining large OOP application code has its advantages and disadvantages compared to maintaining strictly procedural code. There are many sources for learning OO analysis, design and programming techniques which are beyond the scope of this guide.

There are numerous programming languages which incorporate OOP features as extensions or the basis of their language. As such, there are many different terms for describing OO concepts. Even within FPC, some of the terminology overlaps. In general, OOP usually consists of the concept of a programming object (or information unit) which explicitly combines and encapsulates a related set of data and procedures which act on the data. This data is usually persistent during program execution but with mechanisms to reduce some of the problems inherent in declaring global variables. In addition, OOP languages enable objects to be incrementally modified and/or extended based on previously defined objects. This feature is usually referred to by the terms inheritance and polymorphism. Many OOP languages use the terms method or message referring to procedures which belong to an object. Much of the power of OOP is realized by late (run time) dynamic binding of methods rather than compile binding. This dynamic binding of methods is similar to using procedural variables and procedural parameters but with greater syntactic cohesion, encapsulation with the data it is related to and also inheritance of behavior of previously defined methods. The following wiki pages provide a starting point for discovering more about analysis, design and programming in an object orient manner.

Further information

This has only scratched the surface of the topic. For more details, readers are strongly recommended to read the Free Pascal manuals, especially Chapters 5 (Objects) and 6 (Classes)

See Also

External Links