2011年3月29日 星期二

2011年3月27日 星期日

微軟DirectX

DirectX End-User Runtime
Version: 9.29.1973 Date Published: 11/30/2010
SDK
Version: 9.29.1962, Date Published: 6/7/2010

2011年3月25日 星期五

2011年3月24日 星期四

漫談物件導向(十二) - 再談資料繼承

再談資料繼承

先前提到基於封裝性 (encapsulation)最好將資料成員 (屬性) 設定為private,而由成員方法管控資料成員的存取權。在介紹繼承時,也指出宣告為private之成員方法只有自身類別可以使用,那麼子類別會不會繼承父類別的private資料成員?

其實子類別內還是存在著來自父類別的private資料成員,只是不允許直接使用這些資料成員,除非父類別有提供方法來存取這些private資料成員。檢視下列程式碼中,BALL類別的radius資料成員是private性質,BASKETBALL類別繼承BALL類別,自然還是具備radius屬性,只是不能直接取用,因此程式碼第15行作法是不被允許的;但BASKETBALL類別仍可透過繼承自BALL類別的getRadius()方法來取得radius值 (如程式碼第16行)。從現實生活的角度來看,籃球既然繼承球類別,隱含具備半徑屬性是合理的。

1.    public class BALL {
2.        private float radius;
3.        public BALL (float r) {
4.            this.radius = r;
5.        }
6.        public float getRadius() {
7.            return radius;
8.        }
9.    }
10.  public class BASKETBALL extends BALL {
11.      BASKETBALL (float r) {
12.          super(r);
13.      }
14.      public void showinfo() {
15.          System.out.println(this.radius);           //錯誤
16.          System.out.println(this.getRadius()); //正確
17.      }
18.  }

2011年3月23日 星期三

漫談物件導向(十一) - 多型性

多型性polymorphism

維基百科對多型(英語Polymorphism)一詞的解釋是「物件導向程式執行時,相同的訊息可能會送給多個不同的類別之物件,而系統可依據物件所屬類別,引發對應類別的方法,而有不同的行為」。參照下列程式碼,比較能清楚說明多型的機制。

1.        class OOTest {
2.          public static void main(String[] args) {
3.            SHAPE sobj = null;
4.            CIRCLE c1 = new CIRCLE(3.0F);
5.            RECTANGLE r1 = new RECTANGLE(3.0F, 4.5F);
6.            sobj = c1;
7.            sobj.showinfo();
8.            System.out.println ("area = " + sobj.area() );
9.            sobj = r1;
10.            sobj.showinfo();
11.            System.out.println ("area = " + sobj.area() );
12.          }
13.        }

程式碼第3行宣告了一個SHAPE型別變數,但由於SHAPE是個抽象類別,不能建構物件實體,所以sobj物件變數參照初始值設定為null

在物件導向機制中,父類別型別的物件變數允許參照到其子類別的物件實體,在程式碼第6行,sobj參照到CIRCLE類型物件實體c1,所以程式碼第7sobj.showinfo()表示叫用CIRCLE類別之showinfo()方法,程式碼第8sobj.area()表示叫用CIRCLE類別之area()方法。

而當程式碼第9行將sobj參照到RECTANGLE類型物件實體r1之後,程式碼第10sobj.showinfo()表示叫用RECTANGLE類別之showinfo()方法,程式碼第11sobj.area()表示叫用RECTANGLE類別之area()方法。

換言之,JVM在叫用方法時會依據參照變數實際所參照的物件實體,動態連結到正確的物件方法,這種機制稱為多型性 (polymorphism)

This is a circle
area = 28.26
This is a rectangle
area = 13.5

漫談物件導向(十) - 封裝性

封裝性 (encapsulation)

封裝性 (encapsulation)是指將物件的資料成員 (data member) 保護或隱藏起來,防止有未授權而任意更改物件內部資料之情形,以免破壞資料完整性,所有內部資料存取只能透過公開的管道 (成員函式,member function)來存取。

一旦物件實體建構完成,就可使用『物件變數.方法名稱()』方式來叫用類別對外公開的方法。對一個公開的資料成員來說,當然也可使用『物件變數.資料成員名稱』來存取,不過將資料成員宣告為public,直接讓外界存取這樣作法並不恰當,嚴重違反物件封裝性原則 (encapsulation),合理方式是將資料成員設定為私有性 (private),只允許透過統一方法或介面實作來存取得類別的內部資料,這樣才能有效管制資料的使用權,維護物件資源的完整性與存取安全。

通常稱呼用來讀存物件內部資料的函式為取值函式 (getter),用來變更物件內部資料的函式為設值函式 (setter),也有些人使用存取子 (accessor) 和更動子 (mutator) 來稱呼它們。

理想的封裝是強調鬆散耦合 (loose coupling) 而避開緊密耦合,追求高內聚力 (high cohesion) 而避開低內聚力,也就是儘量讓類別本身功能完整獨立,與其它類別的相依性愈低愈好。

2011年3月17日 星期四

漫談物件導向(九) - 介面

介面 (Interface)

介面的概念有點類似抽象類別,但介面內純粹只能有常數型別資料成員宣告及介面方法原型宣告 (沒有實作程式碼),所以介面資料成員只能宣告為public、static及final,而介面方法只能宣告為public及abstract。介面定義的語法如下:

public abstract interface interface-name [extends interface1, interface2, …]{
    public static final data-type data- members-name = values;  // 資料成員宣告
    public abstract data-type interface-methods();   // 類別方法宣告
}

下列程式碼宣告了ISHAPE介面

1.   public abstract interface ISHAPE {
2.       public static final float pi = 3.14F;
3.       public abstract float area();
4.       public abstract float perimeter();
5.       public abstract void showinfo();
6.   }

由於介面之定義、介面方法及資料成員宣告方式都已被嚴格規範修飾字,因此,ISHAPE介面也可簡化如下:

1.   interface ISHAPE {
2.       float pi = 3.14F;
3.       float area();
4.       float perimeter();
5.       void showinfo();
6.   }

另外,介面只能使用常數資料成員 (宣告的資料成員預設都會加上final修飾字),因此所有資料成員都必須給定資料值,同時不允許在程式中變更資料值。

介面與抽象類別之間還有一個差異點,介面之間允許多重繼承,但類別之間只能存在單一繼承。類別要實作介面時,必須在類別宣告別使用implements關鍵字,下圖說明類別與介面間的繼承關係。


以下CIRCLE類別實作ISHAPE介面,在CIRCLE類別中並未宣告pi資料成員,但卻可以使用pi,這是因為CIRCLE繼承了ISHAPE介面的pi資料成員。

1.    public class CIRCLE implements ISHAPE {
2.        private float radius;              // 資料成員
3.        public CIRCLE (float r) {   // 建構子
4.        radius = r;
5.        }
6.        public float area() {
7.           return pi*radius*radius;   // 實作圓面積計算
8.        }
9.        public float perimeter() {
10.          return 2*pi*radius;         // 實作圓周長計算
11.     }
12.     public void showinfo() {
13.         System.out.println ("This is a circle");
14.     }
15.  }

2011年3月16日 星期三

漫談物件導向(八) - 重載

重載 (Overloading) 方法

程序式程式語言之副程式 (函式) 名稱必須唯一,當我們要設計計算平均值之副程式,計算整數平均值副程式與計算浮點數平均值副程式就不能使用相同名稱。物件導向程式語言則提供重載方法機制,允許類別中的方法使用同樣名稱,只要這些方法的參數資料型態或參數個數不完全一樣即可,這樣的機制使得程式設計人員能較少苦惱於方法名稱的設計,以統一的名稱來呼叫。方法重載時會自動根據方法參數列的資料型態及參數個數的個數叫用正確的方法。

下列程式碼中,RECTANGLE類別提供了二個建構子,第一個建構子沒有任何引數,第二個建構子則使用了二個浮點類型的引數。

1.    public class RECTANGLE extends SHAPE {
2.        private float width; // 資料成員
3.        private float height;
4.        public RECTANGLE () { // 建構子
5.            height = 2.0F; // 使用預設的高度值
6.            width = 3.0F; // 使用預設的寬度值
7.        }
8.        public RECTANGLE (float h, float w) { // 建構子
9.            height = h;
10.          width = w;
11.      }
12.      public static void main(String[] args) {
13.          RECTANGLE rect1 = new RECTANGLE ();
14.          RECTANGLE rect2 = new RECTANGLE (5.0F, 10.0F);
15.     }
16.  }

對程式碼第13行而言,RECTANGLE建構子並未給定任何引數,因此會使用程式碼第4~7行的建構子。而以程式碼第14行來說,RECTANGLE建構子伴隨二個浮點引數,因此會使用程式碼第8~11行的建構子。

不管是預設建構子RECTANGLE ()或給定長寬引數的建構子RECTANGLE (h, w),其實要做的事情都類似,差別只有height及weight資料成員設定值不同而已,因此,預設建構子可改寫如下:

1.    public RECTANGLE () { // 物件建構函式
2.        this (2.0F, 3.0F);
3.   }

2011年3月10日 星期四

漫談物件導向(七) - this與super

this與super

super關鍵字表示父類別的意思,程式中可以使用super叫用父類別中被子類別覆蓋的變數或方法。下列程式碼中第9行super.showme()表示叫用父類別PARENT中的print()方法。

1.    public class PARENT {
2.        protected void showinfo() {
3.            System.out.println("invoke the method of class PARENT.");
4.        }
5.    }
6.    public class CHILD extends PARENT {
7.        public void showinfo() {
8.            System.out.println("invoke the method of class CHILD.");
9.            super.showinfo(); // 叫用父類別的showme()方法
10.      }
11.      public static void main(String[] args) {
12.          CHILD objDemo = new CHILD();
13.          objDemo.showinfo();
14.      }
15. }

this則代表物件本身,當你想取得對當前物件參照,可使用this關鍵字。下列程式碼第4行this.name = name,第一個name所代表的是DEMO類別中的資料成員,而等號後面的name則是代表setName()方法中的引數名稱。

1.    public class DEMO {
2.        private String name;
3.        public setName(String name) {
4.            this.name = name; //前一個name是DEMO的資料成員; //後一個name是setName()方法的引數
5.        }
6.    }

再者,super/this與super()/this()二者混淆了。super()表示叫用父類別的預設建構子(沒有引數的建構子),this()則是表示叫用自身類別的預設建構子。

還有一點要注意,當Java建立子類別物件時,JVM會先呼叫父類別的預設建構子,之後才繼續執行子類別建構子的程式碼,換言之,在建構子程式碼的第一行隱含了super()敘述。

以下列程式碼為例,類別B繼承類別A,當程式碼第11行執行建構類別B物件實體時,就會進入類別B的建構子 (程式碼第7行),由於存在著繼承關係,JVM先呼叫執行類別A的建構子 (程式碼第2~4行),之後再繼續類別B的建構子程式 (程式碼第8~9行),因此程式執行結果會輸出"AB"。

1.    class A{
2.        public A(){
3.            System.out.print("A");
4.        }
5.    }
6.    class B extends A {
7.        public B(){ // 會先隱含執行super()敘述
8.            System.out.print("B");
9.        }
10.      public static void main(String[] args) {
11.          new B();
12.      }
13.  }

對上述程式碼而言,類別B建構子程式碼相當於:

1.    public B (){
2.        super ();
3.        System.out.print ("B");
4.    }

super()/this()也可加入引數,表示叫用有引數的建構子。最後要強調的是super()或this()均需放在方法的第一行,還有this和super指的都是物件,所以,不可以在static環境中使用,包括static方法及static區塊。

2011年3月8日 星期二

漫談物件導向(六) - 使用物件

使用物件

先前講過類別定義如同藍圖設計,完成之後還必須依設計圖實作出物件實體 (instance) 才可以使用。在電腦的世界裡,物件是指在執行時期建構,並且佔有記憶體的實例變數。一旦類別完成定義,該類別就可當作是一種自訂資料型別,可用來宣告物件變數。

物件變數是屬於參照變數,變數值記錄著實際儲放物件資料的heap記憶體空間之起始位址。要建立物件實體,必須使用new運算子來建立物件,同時指定建構子參數,其語法如下:

類別名稱 物件變數 = new 物件名稱();

一旦建立了物件,就可使用點運算子叫用成員方法 (物件名稱.方法名稱())。下列程式碼,宣告r1為RECTANGLE類型的物件變數,並參照到一個新建立的RECTANGLE物件實體,之後r1就可以使用點運算子叫用成員方法。

1.    RECTANGLE r1 = new RECTANGLE(3.0F, 4.0F);
2.    r1.showinfo();
3.    System.out.print("the area of rect1 is ");
4.    System.out.println(rect1.area());

漫談物件導向(五) - 繼承

繼承 inheritance

繼承是物件導向重要特性之一,子類別可繼承父類別中宣告為public或protected之方法。就重複使用 (reuse) 的觀點,每個子類別可reuse其父類別的資料結構與程式碼。以繼承方式定義子類別,其語法如下:

modifier class class-name extends super-name{
    declaration of data members // 資料成員宣告
    constructor // 物件建構函式
    definition of class methods // 類別方法定義
}

上述super-name為父類別名稱,若類別定義時沒有宣告類別繼承關係,Java預設新類別是繼承自『java.lang.Object』,所以下面兩行的類別定義是相同。

abstract class SHAPE{…}
abstract class SHAPE extends Object{…}

子類別除了可以使用繼承自父類別的資料成員及成員物件,當然也可宣告新的資料成員、定義新的成員方法、或覆寫 (override) 繼承自父類別的成員方法。有一點要特別注意,Java的類別繼承方式為單一繼承,所以extends關鍵字後只能接一個父類別名稱。

下列程式碼中,RECTANGLE類別繼承自SHAPE類別,並在RECTANGLE子類別中覆寫了area、perimeter及showinfo方法。RECTANGLE子類別還宣告了width及height資料成員,並在建構子中設定資料成員值。

1. public class RECTANGLE extends SHAPE {
2.     private float width; // 資料成員
3.     private float height;
4.     public RECTANGLE (float w, float h) { // 建構子
5.         height = h;
6.         width = w;
7.     }
8.     protected float area() { // 計算矩形面積
9.         return height*width;
10.   }
11.   protected float perimeter() { // 計算矩形周長
12.       return 2*(width + height);
13.   }
11.   protected void showinfo() {
12.      System.out.println("This is a rectangle");
13.   }
14. }

子類別除了繼承父類別的成員方法外,當然也可新增新自己的成員方法。在此我們在RECTANGLE類別中新增一個resize方法,程式碼如下:

1.    public void resize(float rate) {
2.        width = width * rate;
3.        height = height * rate;
4.    }

2011年3月7日 星期一

漫談物件導向(四) - 抽象類別

抽象類別

抽象類別是一種特殊的類別,當類別定義中,存在有任何抽象方法,該類別就變成抽象類別。抽象方法與一般成員方法不同,它只有定義方法的原型而沒有本體,方法的本體 (程式碼) 則是交由其子類別覆寫 (override) 程式碼內容。換言之,抽象方法表示說這個類別具別某種功能,但並未具體描述如何實作這個功能。由於抽象類別內含未實作之函式,所以不允許使用new關鍵字來產生物件實體。

舉例來說,動物都會叫 (sound),猫會喵喵叫,狗會汪汪,牛會哞吽叫換言之,每種動物叫聲並不一樣,因此不適合在動物類別中直接定義sound內容在這種情況下,可將sound方法宣告為抽象方法,也就是說,在動物類別中只能描述它具有sound方法,但不規範sound內容,而是交由動物的子類別 (猫、狗、牛) 各自覆寫sound程式碼。

抽象類別宣告時,必須使用abstract修飾字。而由於抽象方法是要由繼承的子類別覆寫方法的程式主體,所以抽象方法修飾字abstract不能同時與final、static或private修飾字合併使用。

下面程式碼定義了一個SHAPE類別,SHAPE類別內宣告了area、perimeter及showinfo三個成員方法,其中area及perimeter為抽象方法,而showinfo則有實作程式碼。

1. abstract class SHAPE {
2.   protected abstract float area();
3.   protected abstract float perimeter();
4.   protected void showinfo() {
5.     System.out.println ("This is a shape");
6.   }
7. }

SHAPE這個抽象類別規範所有幾何圖形皆應具備計算面積 (area) 與周長 (perimeter) 的功能,因為不同幾何圖形計算方式並不一樣,所以不應該在SHAPE中實作計算面積及周長的程式碼,而是交由任何繼承SHAPE的子類別各自覆寫area方法及perimeter方法

2011年3月6日 星期日

漫談物件導向(三) - 定義類別

定義類別

在物件導向機制下,定義類別就像是設計工程藍圖,而依照設計藍圖實作出來的產品就是物件實體。類別定義用來描述物件的結構與行為,若先不考慮指定繼承關係,類別定義的語法如下:

modifier class class-name {
    declaration of data members  //資料成員宣告
    constructor  //物件建構函式
    definition of class methods    //成員方法定義
}

modifier用來指定類別的存取限制,可以使用以下的修飾字:

public
此種類別可以被任何類別所使用。Java規定一個程式檔內最多只能有一個public類別,而且public類別名稱必須和檔案名稱的主檔名相同。
無修飾字 (預設)此種類別只能被同package內的類別或放置在相同目錄下的類別使用。
final 宣告為最終類別,表示此類別不能用來做為其它類別的父類別。
abstract宣告為抽象類別,此種類別中定義了抽象方法。由於抽象方法只定義函式介面而沒有實作方法內部程式碼,因此抽象類別不能被實體化,但仍可做為其它類別的父類別。
 
class-name為類別名稱,必須是合法的識別字,命名時須注意以下幾點:
1. 第一個字元必須是英文字母、底線符號或$。
2. 名稱沒有長度限制,但有區分大小寫,math與Math將視為不同。
3. 不能使用Java的關鍵字,例如public, abstract, this…等。

右左大括弧內為類別主體,類別主體中可以定義資料成員 (data members)、物件建構子 (constructor)、及方法 (methods)。三者順序並無限制,但為使程式更具可讀性,通常會優先宣告資料成員,接著定義物件建構子,而將其它類別方法置於後。

資料成員用來記錄物件內部的資訊或狀態,有時也稱之為properties或attributes。成員方法則描述物件的行為,也有文件稱之為operations或services。

物件建構子是一個特殊的方法,其名稱必須與類別名稱一樣,而且沒有傳回值 (不是void樣)。當應用程式使用new運算子建立一個物件時,該物件的建構子會最先被執行,因此,會將物件初始化工作放在物件建構子中。

下列程式碼中定義了一個SHAPE類別,在SHAPE類別中宣告了一個成員方法 – showinfo。

public class SHAPE {
    protected void showinfo() {
        System.out.println("This is a shape");
   }
}

成員方法在宣告時可設定其存取層級,有效設定值包括public、protected、package及private,說明如下:

存取層級同一Class同一Package子類別其它Package
private
v
package
v
v
protected
v
v
v
要繼承
public
v
v
v
v

2011年3月5日 星期六

漫談物件導向(二) - 類別與物件概念

類別與物件概念

物件導向程式世界與現實生活中的觀念是相當契合的,對照實際生活中我們處理事情的模式,你會發現物件導向是很生活化的。日常週遭中存在著許多與我們生活息息相關的物件,電腦、原子筆、手機…等,物件也可以是其它物件組合而成,比方說電腦是由主機、螢幕、鍵盤、滑鼠等物件組成;原子筆由筆蓋、筆桿、筆蕊組成。

同一類的物件彼此間或許有些許差異,但其基本功能及與外界互動的模式是共通的。例如各家廠牌的錄音機雖然造型、顏色、輸出功率…等規格有所不同,但按Record鍵錄音、按Play鍵放音等操作方法則是共通的。以物件導向的術語與觀點而言,同一類的物件可以有不同的屬性值 (property values, Java語言稱為fields),但會提供一致的操作方法 (methods)。

而我們也習慣將物件分門別類,被歸屬到同一類的物件都有著共通特性,這如同在生物學上將物種分類為『界門綱目科屬種』七個層次,底層會繼承 ( inherit) 上層的共通特。例如電腦可分為伺服電腦、桌上型電腦、掌上型電腦、筆記型電腦及平板電腦,由於都是屬於電腦類,因此有其共通性,但桌上型電腦、筆記型電腦及掌上型電腦之間還是有些差異;即使同一家廠牌,不同型號電腦規格也有差異,表示不同子類別除了繼承其父類別共通特性外,還可衍生出自己的獨特性

換言之,類別定義有如規格描述,根據類別定義所建置的物件擁有共通的特徴 (或屬性,attribute) 與功能 (或方法,method)。比方說,電腦類別具備算術邏輯運算、資料輸出入、儲存資料等功能,雖然不同型號的電腦產品 (物件實體, instance) 間,規格會有所差異,但其基本狀態與行為還是一樣的。電腦廠商在推出新款電腦時,會先設計產品藍圖 (類別設計階段,class design),下游然後生產線即可依照產品藍圖生產系列電腦產品 (物件實作階段,implementation)。但即使同一型號電腦,電腦商家還是可針對不同消費者進行客制化 (customize),例如可加裝主記憶體、硬碟、或CD-RW等。

另外以個人資料為例,有些資料是屬於私密性質 (private) 並不讓其它人知道,有些訊息則同意公開 (public) 與它人分享,而某些資訊則是有限度的公開(protected),只會讓親朋好友知道,這些概念也都被引用到物件導向程式語言的規範中,類別提供的方法可分為Public、Protected、Package及Private四種存取層級

漫談物件導向(一)

早期C、BASIC、FORTRAN這類程式語言被稱為是程序式程式 (procedural programming),程式的執行流程是依照程式設計師解題步驟及思考邏輯,應用循序、迴圈及分支等三種程式架構,控制程式之執行程序。物件導向程式則是強調利用物件分工與互動,來完成特定的工作,在設計時,每一個類別被賦予特定的功能 (methods) 與特性 (properties),經由不同物件的組合 (分工) 與作用互動,來完成所需的工作。

物件導向是一種程式設計的方法論,目前許多程式語言都支援物件導向,例如C++、C#、Java等,但並不表示使用Java程式語言寫出來的程式就是物件導向程式,使用Java設計程式就稱為物件導向程式設計,最重要的是您是否遵循物件導向程式設計的方法論,尤其多型性 (polymorphism)、繼承性 (inheritance)、及封裝性 (encapsulation) 是物件導向的三個主要觀念,務必要充份了解其內涵,才能正確運用其特性,發揮了物件導向程式的特點。