Clipper 教程:開源 Clipper(s)/面向物件程式設計指南
Clipper 對 OOP(面向物件程式設計,從現在開始我們稱之為它的暱稱)的支援非常有限。它的繼任者 CA-Visual Objects 在這方面要先進得多。然而,Visual Objects 從未取得過巨大成功,第三方生產商為 Clipper 提供了 OOP 庫,其中最著名的是 Class(y)、TopClass、Fivewin 和 Clip4Win。
假設我們正在處理給定點的笛卡爾座標的距離函式 (http://mathinsight.org/cartesian_coordinates)。我們將應用的公式是
分別用於實數線、歐幾里得平面和歐幾里得空間上的(歐幾里得)距離。
在程序式程式設計中,我們的函式看起來像這樣
? distance1d(4,-3)
? distance2d(2,-3,-1,-2)
? distance3d(1,1,1,4,4,4)
FUNCTION distance1d( x1, x2 )
RETURN sqrt((x2-x1)^2)
FUNCTION distance2d( x1,y1,x2,y2 )
RETURN sqrt((x2-x1)^2+(y2-y1)^2)
FUNCTION distance3d( x1,y1,z1,x2,y2,z2 )
RETURN sqrt((x1-x2)^2+(y1-y2)^2+(z1-z2)^2)
我們定義了三個函式,具有不同的名稱,它們以座標作為引數。但這樣做,我們需要為三維空間中的距離傳遞六個引數。
如果我們使用面向物件程式設計來做,我們可能會得到這樣的東西
#include "hbclass.ch"
CREATE CLASS Point1D
VAR Abscissa // the abscissa of our point
METHOD New( Abscissa ) // Constructor
METHOD Distance( Point )
ENDCLASS
CREATE CLASS Point2D INHERIT Point1D
VAR Ordinate // the ordinate of our point
METHOD New( Abscissa, Ordinate ) // Constructor
METHOD Distance( Point )
ENDCLASS
CREATE CLASS Point3D INHERIT Point2D
VAR Zcoord // the Z-coordinate of our point
METHOD New( Zcoord ) // Constructor
METHOD Distance( Point )
ENDCLASS
&& Constructors Zone
METHOD New( Abscissa ) CLASS Point1D
::Abscissa := Abscissa
RETURN Self
METHOD New( Abscissa, Ordinate ) CLASS Point2D
::Abscissa := Abscissa
::Ordinate := Ordinate
RETURN Self
METHOD New( Abscissa, Ordinate, Zcoord ) CLASS Point3D
::Abscissa := Abscissa
::Ordinate := Ordinate
::Zcoord := Zcoord
RETURN Self
&&Distances Methods
METHOD Distance( Point ) CLASS Point1D
RETURN Sqrt( ( Self:Abscissa - Point:Abscissa ) ^ 2 )
METHOD Distance( Point ) CLASS Point2D
RETURN Sqrt( ( Self:Abscissa - Point:Abscissa ) ^ 2 + ( Self:Ordinate - Point:Ordinate ) ^ 2 )
METHOD Distance( Point ) CLASS Point3D
RETURN Sqrt( ( Self:Abscissa - Point:Abscissa ) ^ 2 + ( Self:Ordinate - Point:Ordinate ) ^ 2 + ( Self:Zcoord - Point:Zcoord ) ^ 2 )
PROCEDURE Main()
FirstPoint := Point1D():New( 3 )
SecondPoint := Point1D():New( - 3 )
? FirstPoint:Abscissa
? FirstPoint:Distance( SecondPoint )
ThirdPoint := Point2D():New( 2, - 3 )
FourthPoint := Point2D():New( - 1, - 2 )
? ThirdPoint:Distance( FourthPoint )
FifthPoint := Point3D():New( 1, 1, 1 )
SixthPoint := Point3D():New( 4, 4, 4 )
? FifthPoint:Distance( SixthPoint )
RETURN
在這裡,我們定義了三個類,它們的建構函式,以及每個類的distance方法,並展示瞭如何使用它們。這也是繼承如何工作的簡單示例。其他概念是封裝(資訊隱藏或資料隱藏)、抽象、多型性、過載和覆蓋方法。繼承在關鍵概念:重用中起著核心作用。如果一個類恰好是所需的,那麼它可以在軟體專案中重用;或者,如果它不是完全需要的,可以透過定義一個子類來擴充套件它。就像資料庫的設計一樣,面向物件類的設計也是一門藝術,有其原則,例如參見http://www.oodesign.com/、http://www.codeproject.com/articles/567768/object-oriented-design-principles以及有關 UML 的頁面,如http://www.uml-diagrams.org/uml-object-oriented-concepts.html。
首先要注意的是,我們從包含clipper 頭檔案hbclass.ch開始,這是 Class 命令的標頭檔案。
對物件變數和方法的訪問是透過冒號運算子完成的。字首雙冒號指的是具有更大範圍的變數(例如傳遞給方法的變數)。
在上面的程式碼中,我們定義了三個類,每個類都實現了一個 Point。例如,Point2D 被定義為一個擴充套件 Point1D 的類,即概念的泛化。為每個類提供了一個 Distance 方法。
像這樣的行
? FifthPoint:Distance( SixthPoint )
包含輸出命令?,對一個物件的引用(在本例中為FifthPoint),對 Distance 方法:Distance的呼叫,並將另一個點傳遞給它( SixthPoint )。
也可以編寫一個接受兩個 Point 類引數的 Distance 函式,看起來像這樣
FUNCTION Distance ( Point1, Point2 )
RETURN Sqrt( ( Point1:Abscissa - Point2:Abscissa ) ^ 2 + ( Point1:Ordinate - Point2:Ordinate ) ^ 2 + ( Point1:Zcoord - Point2:Zcoord ) ^ 2 )
? Distance( FifthPoint, SixthPoint )
然而,這不是面向物件程式設計,因為我們可以在非面嚮物件語言(如 Pascal 或 C)中使用相同的函式,並傳遞給它兩個名為 Point1 和 Point2 的struct或record(Pascal 稱之為)。
重要的是,物件內部的一些資料(由該物件的實際程式設計師設定)不能被物件使用者更改。舉一個現實生活中的例子,我們可以考慮汽車發動機。物件的提供者設定了氣缸的數量,我們沒有太多機會改變它:我們必須把它視為一個常數。自然地,關於發動機有很多有趣的公式供工程師使用(一些可以在http://www.thecartech.com/subjects/engine/engine_formulas.htm上看到)。例如,計算發動機容積效率的公式,給定進入氣缸的空氣量和氣缸掃過的體積。這就是資料隱藏的重要性:沒有人需要知道這些資訊才能讓他的汽車行駛。此外,當有人設計發動機時,他們可能不希望使用者透過操作發動機來更改容積效率。在面向物件程式設計中,使用可見性修飾符或訪問修飾符可以獲得相同的效果。
[CREATE] CLASS <cClassName> [ FROM | INHERIT <cSuperClass1> [, ... ,<cSuperClassN>] ]
[ MODULE FRIENDLY ] [ STATIC ] [ FUNCTION <cFuncName> ]
[HIDDEN:]
[ CLASSDATA | CLASSVAR | CLASS VAR <DataName1>]
[ DATA | VAR <DataName1> [,<DataNameN>] [ AS <type> ] [ INIT <uValue> ]
[[EXPORTED | VISIBLE] | [PROTECTED] | [HIDDEN]] [READONLY | RO] ]
...
[ METHOD <MethodName>( [<params,...>] ) [CONSTRUCTOR] ]
[ METHOD <MethodName>( [<params,...>] ) INLINE <Code,...> ]
[ METHOD <MethodName>( [<params,...>] ) BLOCK <CodeBlock> ]
[ METHOD <MethodName>( [<params,...>] ) EXTERN <funcName>([<args,...>]) ]
[ METHOD <MethodName>( [<params,...>] ) SETGET ]
[ METHOD <MethodName>( [<params,...>] ) VIRTUAL ]
[ METHOD <MethodName>( [<params,...>] ) OPERATOR <op> ]
[ ERROR HANDLER <MethodName>( [<params,...>] ) ]
[ ON ERROR <MethodName>( [<params,...>] ) ]
...
[PROTECTED:]
...
[VISIBLE:]
[EXPORTED:]
...
[FRIEND CLASS <ClassName,...>]
[FRIEND FUNCTION <FuncName,...>]
[SYNC METHOD <cSyncMethod>]
ENDCLASS [ LOCK | LOCKED ]
從 w:Harbour (軟體) 中逐字複製
#include "hbclass.ch"
PROCEDURE Main()
LOCAL oPerson
CLS
oPerson := Person():New( "Dave" )
oPerson:Eyes := "Invalid"
oPerson:Eyes := "Blue"
Alert( oPerson:Describe() )
RETURN
CREATE CLASS Person
VAR Name INIT ""
METHOD New( cName )
METHOD Describe()
ACCESS Eyes INLINE ::pvtEyes
ASSIGN Eyes( x ) INLINE iif( HB_ISSTRING( x ) .AND. x $ "Blue,Brown,Green", ::pvtEyes := x, Alert( "Invalid value" ) )
PROTECTED:
VAR pvtEyes
ENDCLASS
// Sample of normal Method definition
METHOD New( cName ) CLASS Person
::Name := cName
RETURN Self
METHOD Describe() CLASS Person
LOCAL cDescription
IF Empty( ::Name )
cDescription := "I have no name yet."
ELSE
cDescription := "My name is: " + ::Name + ";"
ENDIF
IF ! Empty( ::Eyes )
cDescription += "my eyes' color is: " + ::Eyes
ENDIF
RETURN cDescription