Chapter7_成為會使用面向物件程式設計的程式設計師吧
理解面向物件程式設計
熱身問答
- Object 翻譯成中文是什麼?
- 物件
- 物件(Object)是表示事物的抽象名詞。
- OOP 是什麼的縮略語?
- Object Orient Programming
- 面向物件也可以簡稱為 OO(Object Oriented)。
- 哪種程式語言在 C 語言的基礎上增加了對 OOP 的支援
- C++
- ++ 是表示自增(每次只將變數的值增加 1)的 C 語言運算子。之所以被命名為 C++,是因為 C++ 在 C 語言的基礎上增加了面向物件的機制這一點。另外,將C++ 進一步改良的程式語言就是 Java 和 C# 語言。
7.1 面向物件程式設計
面向物件程式設計(OOP,Object Oriented Programming)是一種編寫程式的方法,旨在提升開發大型程式的效率,使程式易於維護。這裡的維護指的是對程式功能的修改和擴充套件。
7.2 對OOP的多種理解方式
面向物件程式設計是一種基於以下思路的程式設計方法:將關注點置於物件(Object)本身,物件的構成要素包含物件的行為及操作B,以此為基礎進行程式設計。這種方法使程式易於複用,軟體的生產效率因而得以提升。其中所使用的主要程式設計技巧有繼承、封裝、多型三種。
那麼, 如何理解OOP呢?
7.3 觀點 1:面向物件程式設計通過把元件拼裝到一起構建程式
面向物件中的重要要素: 類 class
若干個類組裝到一起就構建了一個完整的程式, 所以類是程式的元件/Component, 而面向物件程式設計的關鍵就在於能否靈活地運用類。
那麼, 類是什麼?
我們在之前說過, 無論使用哪種開發方法,編寫出來的程式其內容最終都會表現為數值的羅列,其中的每個數值要麼表示“指令”,要麼表示作為指令操作物件的“資料”。程式最終就是指令與資料的集合
在使用C 語言或 BASIC 等語言程式設計時(它們不是面向物件的程式語言,即不是用於表達面向物件程式設計思想的語言),用“函式”表示指令,用“變數”表示資料。對於 C 語言或是 BASIC 的程式設計師而言,程式就是函式和資料的集合。
/*變數*/
int Variable1;
int Variable2;
int Variable3;
…
/*函式*/
void Function1() { 處理過程 }
void Function2() { 處理過程 }
void Function3() { 處理過程 }
但是在大型程式中, 變數和函式的數量很可能會大量累計, 就可能會導致程式碼凌亂、開發效率低下以及難以維護。所以我們要好好給變數和函式分類, 把程式中有關聯的函式和變數彙集到一起變成類
為了使 C 語言支援面向物件程式設計,人們擴充了它的語法,開發出了 C++ 語言。而通過改良 C++ 又開發出了 Java 和 C#。
class MyClass /*類名*/
{
/*類的成員(變數和函式)*/
int Variable1;
int Variable2;
…
void Function1() { 處理過程 }
void Function2() { 處理過程 }
…
};
7.4 觀點 2:面向物件程式設計能夠提升程式的開發效率和可維護性
在使用面向物件程式語言開發時,並不是所有的類都必須由程式設計師親自編寫。大部分的類都已內置於面向物件程式語言中了,這些類可以為來自各個領域的程式設計師所使用。通常將像這樣的一組類(一組元件)稱作“類庫”。通過利用類庫可以提升程式設計的效率。還有一些類原本是為開發其他程式而編寫的,如果可以把這些現成的類拿過來使用,那麼程式的開發效率就更高了。
企業級的程式通常對程式的可維護性要求較高, 因為將程式投入使用了, 我們一般需要對其已有的功能進行修改和新功能的擴充。
舉例來說,假設我們已經編寫出了一個用於員工薪資管理的程式。隨著薪資計算規則的變更,程式也要進行修改,那麼需要修改的函式和變數就應該已經集中在一個類中了,比如一個叫作 CalculationClass 的類(如圖 7.2 所示)。也就是說,維護時沒有必要去檢查所有的類,只需修改類 CalculationClass 就可以了。(可維護性會在第十二章繼續介紹)
對於創造類的程式設計師, 將什麼抽象為類也是很重要的。 設計類必須要考慮到 當其元件有缺陷了的時候, 這個類也可以被輕鬆替換。
在功能升級後,舊元件能夠被新元件所替換的設計也是必不可少的。因此,創造者和使用者之間就需要事先商定類的使用規範。請諸位記住,對於類的使用者而言“類看起來是什麼樣子的”這種關於規範的描述通常被稱為“介面”(Interface)。
7.5 觀點 3:面向物件程式設計是適用於大型程式的開發方法
就像之前提到的, 假設一個程式需要 10000 個函式和 20000 個變數,使用面向物件程式設計,可以把這個程式用 100 個類組織起來,那麼平均一個類裡就只有 100個函式和 200 個變量了。
7.6 觀點 4:面向物件程式設計就是在為現實世界建模
建模:
- 元件化: 將可看作是由若干種物件構成的集合的現實世界分割成元件
- 省略化: 但是也不必將現實世界100%搬入程式中, 所以我們可以忽略掉一些元件。
舉例來說,假設要為巨型噴射式客機建模,那麼就可以從飛機上抽象歸類出機身、主翼、尾翼、引擎、輪子和座席等元件(如下圖所示)。而像是衛生間這樣的元件,不需要的話就可以省略。“建模”這個詞也可以理解為是製作塑料模型。雖然巨型噴射式客機的塑料模型有很多零件,但是其中應該會省略掉衛生間吧,因為這對於塑料模型來說不是必需的。
7.7 觀點 5:面向物件程式設計可以藉助 UML 設計程式
UML: Unified Modeling Language 統一建模語言
UML幫助我們把對現實世界建模的結果以圖的形式表示回來。
在 UML 中,規定了九種圖。之所以有這麼多種,是為了從各種各樣的角度表示對現實世界建模的結果。
下圖有一個UML類圖的示例, 正是7.3中 Myclass的圖形式的表示。
將一個矩形分為上中下三欄,在上面的一欄中寫入類名,中間的一欄中列出變數(在 UML 中稱為“屬性”),在下面的一欄中列出函式(在 UML 中稱為“行為”或是“操作”)。
在進行面向物件程式設計的設計時,要在一開始就把所需要的類確定下來,然後再在每個類中列舉出該類應該具有的函式和變數,而不要到了後面才把零散的函式和變數組織到類中。也就是說,要一邊觀察作為程式參照物的現實世界,一邊思考待解決的問題是由哪些事物(類)構成的。正因為在設計時要去關注物件,這種程式設計方法才被稱為面向物件程式設計(Object Oriented Programming,其中的 Oriented 就是關注的意思)。而在那些傳統的開發方法中,進行設計則是要先考慮程式應該由什麼樣的功能和資料來構成,然後立即確定與之相應的函式和變數。與此相對在面向物件程式設計的設計中,因為一上來就要確定有哪些類,從而構成程式的函式和變數就必然會被組織到類中。
7.8 觀點 6:面向物件程式設計通過在物件間傳遞訊息驅動程式
假設要編寫這樣一個程式,玩家 A 和玩家 B 玩剪刀石頭布,由裁判判定輸贏。
未使用面向物件程式語言的情況(C 語言):
/* 玩家 A 確定手勢 */
a = GetHand();
/* 玩家 B 確定手勢 */
b = GetHand();
/* 判定勝負 */
winner = GetWinner(a, b);
使用了面向物件程式語言的情況(C++):
// 玩家 A 確定手勢
a = PlayerA.GetHand();
// 玩家 B 確定手勢
b = PlayerB.GetHand();
// 由裁判判定勝負
winner = Judge.GetWinner(a, b);
區別:C++使用了屬於PlayerA的函式GetHand(), 以及屬於PlayerB的函式GetHand();, 還有屬於Judge的函式GetWinner()。
在 C 語言的程式碼中,僅僅使用了 GetHand() 和 GetWinner() 這種獨立存在的函式。與此相對在 C++ 的程式碼中,因為函式是隸屬於某個類的,所以要使用 PlayerA.GetHand() 這樣的語法,表示屬於類 PlayerA的函式 GetHand()。
也就是說用 C++ 等面向物件程式語言編寫程式的話,程式可以通過由一個物件去呼叫另一個物件所擁有的函式這種方式執行起來。這種呼叫方式被稱為物件間的“訊息傳遞”。在面嚮物件語言中所說的訊息傳遞指的就是呼叫某個物件所擁有的函式。
如果未使用面向物件程式語言,那麼可以用流程圖表示程式的執行過程。流程圖表示的是處理過程的流程,因此通常把非面嚮物件語言稱為“過程型語言”。
如果使用面向物件程式語言, 那麼可以使用 UML 中的“時序圖”和“協作圖”表示程式的執行過程。 在時序圖中,把用矩形表示的物件橫向排列,從上往下表示時間的流逝,用箭頭表示物件間的訊息傳遞(即程式上的函式呼叫)。
7.9 觀點 7:在面向物件程式設計中使用繼承、封裝和多型
繼承Inheritance、封裝Encapsulation、多型Polymorphism,這三個是面向物件程式設計的三個基本特性。
- 繼承:通過繼承已存在的類所擁有的成員而生成新的類
- 封裝:在類所擁有的成員中,隱藏掉那些沒有必要展現給該類呼叫者的成員
- 多型:針對同一種訊息,不同的物件可以進行不同的操作。
7.10 類和物件的區別
類是物件的定義,物件是類的例項/Instance。
在之前的7.3節的程式中,定義了一個類 MyClass。但是我們還無法直接使用類 MyClass 所持有的成員,要想使用就必須在記憶體上生成該類的副本,這個副本就是物件.
先建立類的物件然後再使用(C++):
MyClass obj; // 建立物件
obj.Variable = 123; // 使用物件所持有的變數
obj.Function(); // 使用物件所持有的函式
所以我們需要先建立一個個的物件, 然後才能使用類中定義的成員。
7.11 類有三種使用方法
- 僅呼叫類所持有的個別成員(函式和變數)
- 在類的定義中包含其他的類(這種方法被稱作組合)
- 通過繼承已存在的類定義出新的類。
下面有一段C#編寫的Windows應用程式: 當用戶點選按鈕,就會彈出一個訊息框,裡面顯示的是輸入到兩個文
本框中的數字進行加法運算後的結果。
然後我們來關注這裡的類的三種使用方法:
- 在這個程式中,表示整體介面的是以 Form1 為類名的類。類 Form1 繼承了類庫中的類System.Windows.Forms.Form 。
- 在 C# 中用冒號“:”表示繼承。
- 在窗體上,有兩個文字框和一個按鈕,用程式來表示的話,就是類 Form1 的成員變數分別是以類 System.
Windows.Forms.TextBox(文字框類)為資料型別的 textBox1、textBox2,和以類System.Windows.Forms.Button(按鈕類)為資料型別的 button1。- 像這樣類中就包含了其他的類,也可以說是類中引用了其他的類。
- 而程式碼中的 Int32.Parse 和 MessageBox.Show,只不過是個別呼叫了類中的函式。
進行加法運算的 Windows 應用程式(用 C# 編寫):
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Button button1;
...
private void button1_Click(object sender,
System.EventArgs e)
{
int a, b, ans;
a = Int32.Parse(textBox1.Text);
b = Int32.Parse(textBox2.Text);
ans = a + b;
MessageBox.Show(ans.ToString());
}
}
總結
面向物件程式設計就是把元件拼裝到一起進行程式設計的方法。
面向物件中使用的程式設計技巧有繼承、封裝和多型三種。
OOP的關鍵在於能否靈活地運用類。
類是互相有關聯的函式和變數的合集。
而一個類的物件可以通過生成另一個類的物件, 從而呼叫另一個類的函式。 這就叫做物件間的訊息傳遞。
UML幫助我們以圖的形式現實程式語言對於現實世界的建模。