搜尋此網誌

2012/04/27

多執行緒環境下之資料庫存取

多執行緒環境下之資料庫存取

同步化 + Singleton Design Pattern

    存取資料庫是相當耗時的工作,不論是建立連線或做大量資料運算,這些工作皆
遠較單純在程式中運算耗費更多的時間。所以我們有時會想以新的執行緒去處理一些
工作,或是將資料處理在背景中執行。這樣雖然方便,但是也因而產生了一些新的問
題。

    我們知道資料庫中有許多動作(尤其是 Modify)是不允許同時操作的,例如在同一
個時間就不可能由兩個以上的使用者更改同一個表格的同一筆記錄。現在若是我們產
生多個執行緒去異動資料庫,則我們很可能遇到以上的狀況,因而在此提供一種解決
架構。


    在此所提出的解決方式就是同步化(Synchronized)各執行緒對資料庫的存取。我
們可以定義一個共同的介面,所有執行緒皆透過此介面對資料庫存取,而此介面的責
任有二:
  1. 使用 Singleton Design Pattern 實作,也就是永遠保持一個 instance。因為 若是產生多個 instance 則將無法做到同步化。
  2. 同步化(Synchronized)各執行緒對資料庫的存取。
其示意圖如下: Thread1 Thread2 ThreadN | | | | | | o1:SomeObject o2:SomeObject oN:SomeObject | | | | | | ------------------------------------------------- DatabaseFacade ------------------------------------------------- | | Database 要實作出 Singleton 非常容易,以 Java(TM) 為例,只要將所有數建構子宣 告為 private ,而且至少提供一個建構子(防止編譯器提供預設建構子)。然後提 供一個類別(靜態)方法,例如 getInstance() 以供外界取得物件。 程式範例如下: <span style="font-family: Courier New, Courier, monospace;">public class SingletonImpl { // Hold the instance reference. private static SingletonImpl impl; public static getInstance() { if (impl == null) impl = new SingletonImpl(); return impl; } // Prevent external class to create SingletonImpl // instance directly. private SingletonImpl() { // Do nothing } }</span><span style="font-family: arial;"> </span>

Data Backup Module Design


Log into DBM

Show the sequence diagram for logging into DBM.
Because the security mechanism of DBM is based on ACL (Access Control List), thus it is a little different from logging into common system.

Sequence Diagram for Logging Into DBM

Data Backup

Class Diagram

Class Diagram for Data Backup

Description:

The Schedule class manages the schedule of data backup. The schedule’s behaviors depend on its state. This is referred as “State Pattern”. Once you pass proper state to schedule, the schedule will invoke the stored procedure written by Owen and pass proper arguments to this stored procedure to do data backup.
It is very complicated to invoke the stored procedure to perform data backup. For example, the stored procedure has many arguments. Therefore, it is very convenient to wrap this complex into some abstract classes so that client may understand it easily and this abstract layer may also provide flexibility to face requirement changes.
The relevant classes are described as flowing, and you may see the mimic client code in the later section.
No.
Class / Interface

Responsibilities

1.
Info
Record the needed information about data backup.
For example, it will record database name, backup type (Complete, Differential, and Transaction log), backup destination, and so on.
2.
Schedule
Represent a schedule for MS SQL Server data backup. After the schedule is configured properly, you may call its execute method to add a schedule into MS SQL Server.
Each Schedule object contains Frequency object, Info object, and State object.
3.
Frequency
Frequency object manages the main arguments to invoke stored procedure.
In MS SQL Server, the data backup is setup based on frequency concept. This class abstracts this concept to wrap the complex of the stored procedure of MS SQL Server.
4.
State
Interface. The root type of the state of the schedule.
It defines the behaviors of all states. Schedule object will behaves depending on the State object it contains.
5.
RunNowState
In this state, data backup command will be executed immediately.
6.
OnTimeState
Execute data backup at the specified date and time.
7.
RecurringState
Abstract class that represents a recurring backup concept.
8.
DailyState
Represent daily backup state of schedule.
9.
WeeklyState
Represent weekly backup state of schedule.
10.
MonthlyState
Represent monthly backup state of schedule.

Mimic Client Code

public static void main( String[] args ) throws Exception {
Schedule schedule = new Schedule( "My Schedule" );
schedule.setDescription( "This is my test back-up schedule!!" );
Info info = new Info();
info.setType( Info.COMPLETE ); // Complete Backup
info.setDatabase( "POISC" ); // Database to backup
info.setDestination( "POISC_BKUP.dat" ); // Backup file name
schedule.setInfo( info ); // Assign Info to Schedule
// Test 5 backup types.
runNow( schedule );
onTime( schedule );
daily( schedule );
Weekly( schedule );
Monthly( schedule );
}
private static void runNow( Schedule schedule ) throws Exception {
schedule.setState( new RunNowState() );
schedule.execute();
System.out.println( schedule.getFrequency().toString() );
}
private static void onTime( Schedule schedule ) throws Exception {
schedule.setState( new OnTimeState(20030221, 1530) );
schedule.execute();
System.out.println( schedule.getFrequency().toString() );
}
private static void daily( Schedule schedule ) throws Exception {
schedule.setState(
new DailyState(20030221, Schedule.Frequency.FI_NONE, 1530) );
schedule.execute();
System.out.println( schedule.getFrequency().toString() );
}
private static void Weekly( Schedule schedule ) throws Exception {
int intvls[] = new int[] {
Schedule.Frequency.FI_SUN,
//Schedule.Frequency.FI_MON,
Schedule.Frequency.FI_TUE,
Schedule.Frequency.FI_WED,
//Schedule.Frequency.FI_THU,
Schedule.Frequency.FI_FRI,
//Schedule.Frequency.FI_SAT,
};
schedule.setState(
new WeeklyState(20030221, Schedule.Frequency.FI_NONE, 1530, intvls) );
schedule.execute();
System.out.println( schedule.getFrequency().toString() );
}
private static void Monthly( Schedule schedule ) throws Exception {
schedule.setState(
new MonthlyState(20030221, Schedule.Frequency.FI_NONE, 1530, 5) );
schedule.execute();
System.out.println( schedule.getFrequency().toString() );
}

Export Data

This function lets user to download data with CSV file format (which can be opened by MicroSfot Excel Application). User may select data according to several criteria.
We must support date export by 3 criteria: 1) by Lot ID, 2) by Updated Time, and 3) by Record Number. All data query methods are almost similar; the only different part is the SQL WHERE conditions. So it is reasonable to have a general abstract class to be the top most class of all the subclasses.
Class Diagram for Data Export

物件導向 VS. 關聯式資料庫

物件導向 VS. 關聯式資料庫

    關聯式資料庫風行已久,之前許多 Client/Srever 程式皆是建構於此種以 SQL
為標準溝通語言的資料庫的基礎上。因為 E-R Model 等理論的發展,其提供一較為
完整之組織資料的方式,所以造成關聯式資料庫的流行。

    但是關聯式資料庫現在也遇到了一些問題,尤其是在與物件導向系統之結合上。
由於慣於使用關聯式資料庫的結果,我們在開發系統時很自然就會以關聯式資料庫的
概念來思考,結果往往會太過以資料庫為中心,而非以問題領域為中心。這和另一種
以程式碼為中心的哲學,恰為及兩個極端的對比。而這二種哲學與物件導向哲學一向
是格格不入的,因為物件導向是以問題中的概念(或物件)為中心,所有的檔案與資料
庫只是一個儲存體(Storage)而已。任何以程式碼或資料庫為中心的架構,皆會將我
們導向一種極不自然或機器式的思考,也因而無法建構出物件導向系統那樣有彈性、
富可理解性、可再使用性、易維護性、以及易於擴充性等的系統。

    關聯式資料庫是以記錄(Record)及欄位(Field)為單位,雖然一個資料庫中的表
格(Table)很像物件導向中的類別(Class),但是我們終究不能直接把表格中的記錄當
作是物件來使用,所有對關聯式資料庫的操作皆必須轉化成SQL中的欄位等格式。
SilverStream 中嚐試用 AgpData 及 AgcData 等資料快取物件來解決此問題,但是
它反而造成另一個問題 -- 使用者介面與處理規則的耦合度太高。例如,大家常把處
理規則寫在 Page 或 Form 中的事件處理,然而因為 SilverStream 事件處理機制是
以 Page 或 Form 為代理者,JavaBean 的事件處理程式碼被指定為  Page 或 Form
的方法,如此方式雖較簡便易寫,但也必定會造成使用者介面與處理規則的高度耦合
,也就是說這個系統的維護性與擴充性會降低至極低,簡言之,就是一個近乎寫死的
系統設計。

    關聯式資料庫與物件導向的問題仍然持續在研究之中,一些關於此方面問題的
Design Patterns 亦有人已發表。不過現在看來,物件式資料庫好像是大家比較注意
的焦點,因為它乾脆放棄對關聯式資料庫的修改,直接提供一個物件式的解決方案。
這或許是一個值得注意的方向!

UML 模型化

UML 模型化

UML 是一種模型化語言 (Unified Modling Language),它提供了一套符號規則給從
事物件導向分析與設計時使用。它使得以物件導向方式開發軟體的團隊一個標準的溝
通方法,同時也讓開發者與使用者可以透過 use case diagram 溝通。

UML 真正的精髓不是在那些圖形與符號,而是在那些圖形與符號之下所蘊含的語意。
事實上 UML 底下隱藏著軟體開發的哲學,若是只學到 UML 的圖形與符號是沒有多大
用處的。我們真正要學的是物件導向分析與設計中的抽象化、模型與問題領域及解法
領域、...等等的方法論,如此才可能增進分析與設計的能力,否則與使用繪圖軟體就
沒有什麼差別了。同時若是你已有物件導向程式設計的經驗,UML 將是一個增進你分
析能力,與再精煉物件導向設計能力的一種工具。畢竟,UML 所提到的東西早已存在
,以前只是缺乏一個統一且有系統的方式來表達,今天 UML 成為標準後,許多觀念
將更容易溝通,而不易產生誤解。

UML 以一種漸進的方式來瞭解系統。首先開發者與使用者使用 UML 從使用者的觀點
來瞭解問題(use case diagram 與 activity diagram)。然後開發者使用 UML 並以
其自己的觀點來瞭解問題(class diagram, sequence diagram,...)。這些清楚的瞭
解使得開發者可以使用 UML 記錄他們發展出的解決方案。最後,UML 會變成系統實
作時的資源。

如果將軟體開發比喻為蓋房子,那麼 UML 的產出文件就好像建築師所繪的建築圖,
而軟體實作者就好比土木承包商。建築師將構想與設計表達於建築圖中,而土木承
包商使用其現有的技術,自由地將建築的模型實現出來,但是土木承包商必須按圖
施工。如此,建築師可以就其專業來設計房屋架構其美學表現,而土木承包商亦可
使用其善長之技術實作之,二者皆可充分發揮其各自的專業,但是又可以互相合作。
傳統上,土木承包商或許可以不需建築師的幫助而自行蓋好一棟小房屋,但是若是
要蓋商業大樓,其間結構的安全等問題則非土木承包商所擅長。今天的軟體愈趨複
雜,若是我們還像以前請土木承包商蓋房屋的方式開發軟體,那麼其後果可想而之。

雖然在小型軟體使用軟體工程的方式影響並不大,但是若不在小型軟體中練習軟體
工程的作法,將來在大型系統必然會手忙腳亂,反而增加失敗的機會。

在使用 UML 開發系統時有一些步驟,在此粗略敘述如下:

需求蒐集 (Requirements Gathering)

在此步驟時,開發者專注於從使用者的觀點來瞭解問題,這時不會考慮技術或設計的問題。如此方可確保開發者能專注於正確的問題,避免對於問題錯誤的瞭解,導致系統後來重大的變更。 在此有一點必須要說明,那就是 Use case 不是物件導向分析與設計的一部分,它只是描述使用者透過系統得到價值的一種敘事性的描述。所以一般使用者也應該看得懂。而開發者與使用者即透過此種方式溝通。 此時可能產生的有:Use Case Diagrams, Text Use Case descriptions, Activity diagrams

系統分析 (Analysis)

在此步驟時,開發者以他們自己的觀點對問題進行分析。此時仍然不會考慮技術的問題。這時工作的焦點在於發現系統中角色(物件),及這些角色(物件)的責任(Responsibilities)。此階段的工作成果將作為後面技術選擇及設計的基礎。此階段的工作屬於概念性(Conceptual)的工作,簡單的說,就是發現物件並找出各物件之間的關係,同時指派各物件的任務。此時產生的 UML 文件可能是草稿的形式,純粹表達出對系統的抽象概念,與任何資訊技術或程式語言沒有關係。
此時可能產生的 UML 有:Class Diagrams, Sequence Diagrams, Collaboration Diagrams

技術選擇 (Technology Selection)


顧名思義,此階段的工作是要選擇適當的技術來實現系統需求。此步驟若要做的好,必須對當前技術有相當的瞭解,例如某種技術的特點,有何優點及缺點等,皆必須有相當的認識,否則將造成後來許多問題。
此時沒有 UML 文件產生。

決定系統架構 (Architecture)


將系統猜拆成若干子系統。此時是以一個非常高階的觀點來拆解系統。可能按照 系統間的關係拆解,也可能依照技術因素拆解。
此時可能產生的 UML 圖有: Primarily Class Diagrams, Package Diagrams

設計與實作 (Design and Implementation)

此時以前述步驟的基礎,進行系統類別的設計與實作。此時會將前面所產出的文件更加精製化,以作出完整的 UML 文件。此時也是本發展循環中,最後一次更動系統架構的機會。我們會使用許多設計樣式(Design Pattern)作出具體的類別架構設計,當一切完成時,即選擇一物件導向語言實作系統。同時,在這一連串的步驟中所 產出的文件,可做為系統測試的依據,及系統發展進程的指標。
事實上,這些步驟不一定是循序漸進進行,可能有若干步驟會同時進行,而也不是每
一種 UML 文件都要產生。重點是不要太過拘泥於形式,而是要學習及掌握物件導向
分析與設計的精神與方法。畢竟,在 UML 誕生之前,物件導向方法即已存在,UML 只
不過是將一些不錯的方法整理起來而已,以提供一套大家可以用來溝通的符號罷了。

記住: UML 可以讓你以一套規則模型化系統,但是它不會教你如何去模型化系統,也 不會教你如何實做出有彈性的系統!不會教你如何實做出有彈性的系統!

物件導向分析、設計、與程式 - OOA/D/P

物件導向分析、設計、與程式 - OOA/D/P

企業流程與軟體發展現況

企業流程是任何企業的資訊應用系統所要處理的主要對象,企業運用資訊系 統完成其應有的程序,以獲得其利益,如客戶下訂單及是一個例子。此過程在人 們看來應是極自然的事情,但是往往在資訊系統上卻非如此。傳統系統分析師在 分析系統時,總是千方百計地要把一些企業流程上的概念,如客戶、訂單、銷售 等概念徹底分解成資料庫表格中的欄位,結果像「客戶」這樣自然的概念,就變 成一堆欄位的組合。因而造成程式設計師在實作系統時,往往變成以欄位作為程 式設計的對象。我相信「一堆欄位的組合」絕對不會比「客戶」容易思考與理解 吧!尤其當現在企業流程日益複雜,那「一堆欄位的組合」可能代表著成千上萬 的欄位組合,我覺得我不是超人,無法完全掌握這些欄位,我也相信應該無人能 完全掌握。重點是,欄位的思考方式是極不自然且難以理解的;因為企業流程中 是以「客戶」、「訂單」、「銷售」等概念來進行的,絕對不是以欄位來進行。 相信沒有一個流程的作業人員會說他是以欄位的觀念來進行工作的。但是很可笑 的是,現在多數的資訊人員卻真得是以欄位來思考整個系統。 讓我們發揮想像力來思考一下,若是以欄位來思考系統會變得如何?第一個 問題恐怕就是難以理解系統。試想,若是我們老是在思考系統時,一直在想欄位 的對應,原本是個具有整體意義的概念被我們拆得支離破碎〈變成一堆欄位〉, 如此相信分析到最後,連我們自己也很難搞得清楚。結果就糊里糊塗地進入實作 階段,因而造成了實作的問題。怎麼說呢?因為在這種情況下,程式碼很容易就 以欄位為對象來撰寫,但是要知道,企業流程是由多個具有整體意義的概念,透 過一連串的規則而完成。這些概念在世界上差異不大,但規則會隨著時空環境而 改變,所以當企業流程改變時,這時以欄位為對象的程式碼根本就難以跟上,因 為所有的概念與規則早已糾結在一團,如何能更改?即使有人真的有耐心去更改 ,因為系統的難以理解,即使改好了當時要的部分,但是有誰真能保證其他部分 沒有受傷到影響? 諸如此類的問題在現實中層出不窮,如此更別說系統再發展等的問題了。所 以讓我們來看看物件導向方法論是否能在這裡貢獻一些力量。

物件導向分析與設計(OOA/D)

回想上一段所提到的:傳統的分析設計方法很容易做出難以理解的系統,因 為那樣的思路是不自然的。物件導向方法論基本上就提供了一個非常自然的方式 讓我們可以用自然的方式思考與瞭解企業流程。在物件導向中,系統中本就存在 「客戶」、「訂單」、「銷售」等概念〈術語就是類別或物件〉,這些物件抽象 及表達現實世界中的概念,同時這些物件不僅是靜態地存在著,它們還是「活生 生的」東西,讓我們可以要求它們做某些動作。系統中所有的物件以一種精心設 計並且符合企業規則的關係來互動,使得一切事物就好像真實世界一般。企業流 程就在如此自然易於理解的方式下完成。 再者,由於物件本身就表達出了一個整體概念,任何關於一個「客戶」的資 訊都封裝在一個「客戶」物件中。還記得前面所提到概念比較不會變〈相信世界 上所有的訂購都有「訂單」這個概念吧!〉,而規則較易改變的現象嗎〈例如三 不五時來個促銷〉?由於物件導向在概念的整體性,所以物件導向系統就比傳統 系統更能適應現在多變的企業環境,且更易維護、修改、與發展了。因為一切都 是那麼自然。

物件導向程式(OOP)

也許物件導向方法較令人難以接受的是:物件導向並不保證導引出容易寫的 程式碼。而且物件導向的觀念也讓許多對結構化程式有經驗的程式設計師難以接 受。 在物件導向系統的設計與實作階段,我們必須具備對物件導向觀念的瞭解, 同時我們也必須熟練一些其他專家已經發展出來的問題解法(Design Pattern), 當然了!我們也必須選擇及熟練一個支援物件導向的程式語言。但是克服這些困 難是值得的,因為我們從此將獲得一個易於理解、維護、與擴充的系統,而這樣 一個系統所省下的成本,絕對比提升資訊人員物件導向觀念的成本還要划算的多 。同時物件導向系統擁有繼承的特性,所以並非所有人員都要熟悉高深的物件導 向觀念,系統的架構可由較具經驗的設計師完成,此架構即可用來規範全體人員 的程式碼,而較無經驗的程式員則可以負責填滿實作,如此則可以解決物件導程 式較難撰寫的問題。

結論

總之,物件導向方法讓我們可以用一種極為自然的方式來看待系統,我們對 系統所做的分析,產生出來的系統模型,都與真實的企業流程相互對應,如果我 們再多花一點想像力,許多在真實世界中抽象的概念也都可以用物件表達出來。 事實上,在物件導向分析實務中,發現隱藏的概念亦是非常重要的任務,由這一 點來看,物件導向同時也提供了一個讓我們驗證對系統的了解是否完整的機會。 此後,我們對系統的了解將是由許多完整的概念與規則所組成,再也不是那種支 離破碎的欄位組合。我們從此可以真正專心在企業流程上,研究出真正適當且符 合使用者需要的流程,降低系統在維護與再發展的成本,從而企業才真正能由軟 體系統獲得最大的利益。這麼多的好處,為什麼不做呢?
一個簡單的範例說明: 底下以一個非常簡單的例子說明。 假設我們要計算某一張訂單的應付總額,而此應付總額可能隨著不同的促銷 時段而定。例如在促銷期間一律打八折,而一般期間不優待。 若是以傳統寫法可能會如此(以下是虛擬碼): &lt;font face="'Courier New', Courier, monospace" size="2"&gt;function compute() { if 一般期間 then 計算訂單的應付總額... else if 折扣期間 then 計算訂單的應付總額... 計算訂單的實際應付總額... }&lt;/font&gt; 但是若我們再訂出更多的時段,每個時段有不同的折扣會如何?結果我們勢 必加入更多的if..else判斷式。不過,若是我們在每個折扣時段訂出更為複雜的 折扣方式時,這個判斷式將會愈來愈龐大而複雜。如果再將折扣的對象改為依產 品與時段而定的話,那麼修改的工作就複雜多了。 &lt;font face="'Courier New', Courier, monospace" size="2"&gt;function compute() { if 一般期間 then 計算訂單的應付總額... else if 折扣期間1 then 計算訂單的應付總額... 計算訂單的實際應付總額... else if 折扣期間2 then 計算訂單的應付總額... 計算訂單的實際應付總額... ... }&lt;/font&gt; 反觀物件導向方式會如何看這個問題?(以Java為例子) 類別定義: 我們先依企業流程將我們所注意的概念以類別(Class)表達出來。 // 定義一個抽象類別表達出訂單的概念&#65292; // 同時用以規範其子代類別必須有責任計算其應付總額 - getPayment()&#12290; &lt;font face="'Courier New', Courier, monospace" size="2"&gt;abstract class Order { abstract double getPayment(); }&lt;/font&gt; // GeneralOrder類別繼承自Order類別&#12290; // 注意&#65306;此時GeneralOrder類別必須實作getPayment()方法&#65292; // 否則根本無法編譯&#12290; &lt;font face="'Courier New', Courier, monospace" size="2"&gt;class GeneralOrder extends Order { double getPayment() { 傳回一般期間的應付總額 } }&lt;/font&gt; // SpecialOrder類別繼承自Order類別&#12290; // 注意&#65306;此時SpecialOrder類別必須實作getPayment()方法&#65292; // 否則根本無法編譯&#12290; &lt;font face="'Courier New', Courier, monospace" size="2"&gt;class SpecialOrder extends Order { double getPayment() { 傳回折扣期間的應付總額 } } &lt;/font&gt; Client 端應用: 此處之Client不是指Client-Server架構下的Client,它只是單純地表示任何 可能使用我們前面的類別定義的潛在程式碼。 // 執行企業規則 &lt;font face="'Courier New', Courier, monospace" size="2"&gt;void processBusinessRules() { // 由於SpecialOrder與GeneralOrder皆是Order的子代類別&#65292; // 所以SpecialOrder與GeneralOrder可以當作compute的參數&#12290; // 這很像人類的觀念&#65306;一般訂單與特殊訂單都是訂單的一種&#12290; if 一般期間 then compute(new GeneralOrder()); else if 折扣期間 then compute(new SpecialOrder()); .... }&lt;/font&gt; &lt;font face="'Courier New', Courier, monospace" size="2"&gt;void compute(Order o) { // 一律要求Order自己去計算應付總額 // 折扣的資訊已經存在於Order的子代類別中 o.getPayment(); }&lt;/font&gt; 雖然物件導向看來使程式碼變多了,但是仔細看看,大部分的程式碼都是在 定義訂單的概念。反而是真正要計算應付總額的地方變得極為簡潔。(我們可以 與前面比較compute的差別)此後即使我們增加不同的時段,更改的地方只是多加 一些Order的子類別,並適當的傳入compute而已,這樣的改變並不會影響其他程 式碼。若是將折扣的對象改為依產品與時段而定的話,只要設計Product等一系 列的類別架構即可輕而易舉地解決。(因為訂單是由產品所組成的) 事實上,若是我們善用一些已被發展出來的設計樣式(Design Pattern)則可 以將Order的子類別物件的產生加以封裝及抽象化,降低客戶端程式與產生物件 程式碼的耦合度,如此將可保留物件產生的彈性,當我們所面臨的情況改變時, 加入新類別或修改舊的類別後,客戶端程式才不會受到影響,或將影響降至最低 。諸如此類的方式或手法,你可以參考設計樣式的專書,那裡會提供非常多的解 法,使得整個系統變得非常有彈性,易於維護。 這樣的做法是否非常符合人類的思路呢?因為企業流程是依照人類的思路所 構思出來的,而物件導向讓我們可以用人類的思路來思索企業流程,所以企業流 程與物件導向是個天生的絕佳組合。而正因為是絕佳組合,所以物件導向方法可 以輕易地追隨企業流程的變化。

淺談物件導向

淺談物件導向

物件導向是現代的顯學,相信任何程式設計師都聽過物件導向,而物件導向也
一直在持續進展中,從之前的原始碼的物件導向,到今日二進位層級的物件導向,
或許物件導向這個東西還會發燒好多年。

基本上,物件導向程式必須實作出幾個觀念:
1. 封裝 (Encapsulation)
將資料與其相關操作(method)包裝在類別中。外界只需叫物件做事,而不用 管物件是如何做到的。如此使得程式更為模組化,物件藉由其界面與外界溝通, 進而增進程式的可維護性,同時也將可能的 bug 關在一個有限的範圍。
2. 繼承 (Inheritance)
子類別可以繼承父類別,而不必重複撰寫與父類別相同的程式碼。
3. 多形 (Polymorphism)
子類別可以覆載(override)由父類別繼承而來的 method ,使得同樣的方法 呼叫,對子類別及父類別的實體(instance or object),會有不同的效果。子類別 雖然繼承父類別的程式,但亦可覆載掉不合用的部份,不會被父類別框住。
4. 抽象化 (Abstraction)
允許建立抽象類別(Abstract Class),定義出其子類別的共同特性。
事實上,物件導向是一個軟體向硬體學習的例子,如果我們觀察各種硬體設備, 我們會發現硬體設備早就是物件導向的系統,而物件導向中許多觀念也真的是向工程 學習而來。
然而我們該如何寫出物件導向風格的程式呢?簡單的說來,有幾個步驟要進行:
1. 辨認出問題中的概念(或物件)
這是物件導向的第一步,我們必須先找到問題領域(Problem Domain)中的相 關物件。早期是靠一些直覺與經驗,而現在物件導向分析的方法論,已提 供一些更可靠的方式,例如 UML 中的 Use Case、Conceptual Model ...等 皆是幫助我們找到物件的好方法。
2. 找出各概念(或物件)間的互動方式
在這裡我們必須決定出物件之間要如何互動,每個物件必須完成什麼任務, UML 中提供一些圖示化的方法,而有豐富經驗的程式設計師亦歸納出一些設 計上的樣板(稱為 Design Pattern)供參考。事實上,這個步驟是最需要創 意,也會花費最多時間與心血的地方,因為這裡的成果很可能就是軟體開發 的成敗關鍵。而不幸的是,正如同世界上沒有一種解題方法可以解決所有的 問題,這裡也不會有(未來也可能沒有)一種萬靈丹式的解法。
3. 將各概念(或物件)化為實際的類別並實作之
選定特定的程式語言,將前面所作的分析與設計的成果,化為實際的程式碼 。這時我們要根據所選定語言的特性,寫出實際的程式碼,其成果的好壞端 賴我們對程式概念的了解以及對於電腦科學的相關知識而定。
所以只是使用 C++ 或 Java 並不表示就是在寫物件導向的程式,物件導向的程
式要求我們以物件導向的方式思考問題,將問題以物件來作為分割的單位,而不是以
功能來分割(以前程序導向的觀念)。如此才有可能在大型軟體中達到最高的穩定性,
可維護性,...之目標。同時這也是邁向分散式計算架構的基礎,因為現在 CORBA 與
COM/DCOM 標準都是物件導向型式的標準,如果不了解物件導向,那就更不可能了解
CORBA 與 COM 這些以 Binary 為基礎的物件導向標準。

物件導向的觀念正持續演進中,現在仍然有許多問題要克服,例如現在風行的關
聯式資料庫,如 Oracle 與 SQL Server 等,它們本身並無物件導向的觀念,所以對
於必須存取關聯式資料庫的軟體來說,就需要更多的設計技巧。所幸目前物件式資料
庫已開始商業化,未來這種問題必然可以獲得改善。

抽象化 - Abstraction

抽象化 - Abstraction

抽象化是物件導向的精髓,以下談談抽象化...

抽象(Abstraction)是複雜概念,程序,或真實世界的一種簡化或是模型化。身為人類
的我們必須依賴抽象化才能生存。就像我們從來不會去瞭解電腦、電視等設備內部的細
節,我們看待這些設備的方式是抽象的,我們只是把它們視為一種可以提供某些服務的
東西而已。也因為如此,這些設備的廠商可以自由實作其內部機制,只要留下一個介面
(如: 遙控器)給我們即可。

而在物件導向中,抽象化是其精髓。藉著抽象化,多型才能發揮到極致,才能將系統的
變動隔絕於外。藉由抽象化,程式碼不會與特定型態的物件產生太緊密的耦合,如此才
能允許我們容易更動特定物件的的實作部分。

舉個例子來說,假設我們有一個函式 foo,它會要求車子物件行駛,但是車子有許多種,
其內部行駛的機制都不盡相同。如果我們為每一種車子寫一個函式,看來好像是物件導
向的寫法,雖然一些語言都支援函式overload,但是此後我們必須一直維護這個函式,
只要新的車子種類加入,我們就要加入一個overload的foo函式。

void foo(ToyotaCar car) {}
void foo(NissanCar car) {}
void foo(BensCar car) {}


這個例子的缺點就是抽象化不夠,系統設計模擬真實世界太過頭了。在物件所處的虛擬
世界中,與真實世界不同的是,所有的物件都是「活的」。較好的設計應該善用抽象化
,先定義一個抽象類別 Car,用以表示車子所共有的概念,然後加入一個抽象方法 run
讓子類別override,如此foo函式永遠將特定的車子視為 Car 來處理,而不需要理會它
是那一種車子,這是否與我們只會按電源來啟動電腦,而不會去理會我們正在使用那一
廠牌那一型號的電腦一樣。

abstract class Car {
    // Concrete subclass SHOULD override this.
    abstract void run();
}

class ToyotaCar extends Car {
  void run() {
      // Do something...
  }
}

class NissanCar extends Car {
  void run() {
      // Do something...
  }
}

class BensCar extends Car {
  void run() {
      // Do something...
  }
}

void foo(Car car) {
    car.run();
}
此後我們即可用以下的方式呼叫 foo: foo(new ToyotaCar());foo(new NissanCar());foo(new BensCar()); 注意,不論我們傳入什麼車種,foo 皆可運作,只要傳入的是 Car 的子類別即可。 你是否已體會到了抽象化的精神呢?沒錯!只要我們小心規劃抽象的介面,很容易就可 以將一些變動隔離出來。後來即使加入程式碼,也不會干擾到現存的程式碼。