- 面向對象方法使構建系統更容易,因爲:
- 解決正確的問題
- 正常工作
- 易維護
- 易擴充
- 易重用
- 大家發現面向對象更易理解
- 實現可以更簡單
- 把數據和功能組合在一起簡單而自然
- 分析和實現之間的概念跨度更小
- 設計良好的一組對象能彈性地適應重用和變化
- 可視化模型提供更有效的溝通
- 建模過程有助於創建通用詞彙以及在開發者和用戶/客戶之間達成共識
- 非計算機編程人員也能理解對象模型
- 你能賺更多錢 :^)
本課程不能把你變成優秀的面向對象設計者,這隻有靠經驗和聰明的頭腦才能做到。
- 學習面向對象範例(paradigm)
- 學習UML的一個子集,用於可視化對象建模
- 學習爲什麼一些設計好於另一些
- 學習怎樣用C++和Java實現面向對象設計
- 學習一些面向對象設計模式
- 瞭解一些最新、最好的商業面向對象技術
- 瞭解一個能最大限度利用面向對象技術的過程
- 爲進一步的學習做好準備,開始着手一個面向對象項目
- 更加深入的知識需要參加java培訓才能快速學習到。
- 模型(Models)
- 對象(Objects)
- 類(Classes)
- 封裝(Encapsulation)
- 抽象(Abstraction)
- 繼承(Inheritance)
- 多態(Polymorphism)
- 泛化(Generalization)
- 系統由過程(procedures)組成
- 過程之間互相發送數據
- 過程和數據各自獨立
- 集中於數據結構、算法和運算步驟的先後順序
- 過程經常難以重用
- 缺乏具有較強表現力的可視化建模技術
- 分析與實現之間需要進行概念轉換
- 本質上是機器/彙編語言的抽象
- 從設計模型到代碼實現跨度很大
- 系統由對象組成
- 對象互相發送消息(過程調用)
- 相關的數據和行爲緊密地綁定在對象中
- 把問題領域建模成對象,要解決的問題自然的映射爲代碼的實現
- 可視模型表現力強,相對容易理解
- 集中於實現之前所確定的職責(responsibilities)和接口
- 強有力的概念:接口,抽象,封裝,繼承,委託(delegation)和多態
- 問題的可視模型逐漸進化成解決方案模型
- 設計模型與代碼實現之間跨度較小
- 努力縮減軟件的複雜度
- 過程/函數化方法
float c = getTemperature(); // 假定爲攝氏度 Celsius
float f = toFarenheitFromCelcius( c );
float k = toKelvinFromCelcius( c );
float x = toKelvinFromFarenheit( f );
float y = toFarenheitFromKelvin( k ); - 面向對象方法
Temp temp = getTemperature();
float c = temp.toCelcius();
float f = temp.toFarenheit();
float k = temp.toKelvin(); - 包含有數據的Temp的內部單元是什麼?
- 成功的程序能解決真實世界的問題
- - 它們緊密對應於需要解決的問題
- - 它們對問題領域和用戶活動進行建模
- 建模促進與用戶更好的可視化交流。成功的面向對象設計總是一開始就由領域專家和軟件設計者建立一個 反映問題領域的可視化的“對象模型”。
- 你願意讓承包人在沒有設計藍圖的情況下建造你的新房子嗎?
- 建模的本質展示所有精確相關的細節
- 代表真實或抽象的事物,有一個名字
- 有明確的職責(well-defined responsibilities)
- 展示良好的行爲(well-defined behavior)
- 接口清晰,並且儘可能簡單
- 自相容,內聚,完備(self-consistent,coherent,and complete)
- (通常)不是很複雜或很大
- 只需要理解自己和一小部分其他對象的接口
- 與一小部分其它對象協同工作(team players)
- 儘可能地與其它對象鬆散耦合(loosely coupled)
- 很好地文檔化,以便他人使用或重用
- 對象是類的實例,每一個對象都有唯一的標識
- 類定義一組對象的接口和實現,即定義了這些對象的行爲
- 抽象類不能擁有實例
- 只要有抽象類(如寵物),通常就會有能夠實例化的具體類(如貓,狗等)
- 一些面嚮對象語言(如Smalltalk)支持元類(metaclass)的概念,程序員可以隨時(on-the-fly)定義一個類,然後實例化。這種情況下,類也是一個對象,即元類
- 對象一旦實例化,就不能更改它的類
- 有唯一標識
- 可以分成許多種類(即類)
- 可以繼承或聚合
- 行爲、職責明確
- 接口與實現分離
- 隱藏內部結構
- 有不同的狀態
- 可以提供服務
- 可以給其它對象發送消息
- 從其它對象接收消息,並做出相應響應
- 可以把職責委託給其它對象
- 有公共的屬性和行爲的一組對象可以抽象成爲類
- 對象通常根據你所感興趣的屬性而分類,
- 例如:街道,馬路,高速公路...
- 不同的程序對它們分類也不同
- 交通模擬器程序
- 單行道,雙通道,有分車道的,住宅區的,限制通行的
- location w/ respect to business commuters.
- 維護調度程序
- 路面材料
- 重型卡車運輸
- location w/ respect to congressional district.
- 類本身也可以有屬性和行爲
- 例如:養老金管理程序中的“僱員”類
- 僱員總數
- 僱員編制多少
- 不同語言對類的支持略有不同:
- Smalltalk 把類當作對象(很有好處)
- C++提供最小限度的支持(有時會帶來很多煩惱)
- Java位於上述兩者之間
- 類也是對象
- 類可以有屬性
- “僱員”類可以有一個包含其所有實例的列表(list)
- “彩票”類可以有一個種子(seed)用於產生隨機票號,該種子被所有實例共享
- 類可以有行爲
- “僱員”類可以有 getEmployeeBySerialNum 行爲
- “彩票”類可以有 generateRandomNumber 行爲
- 只暴露相關的細節,即公有接口(public interface)
- 封裝什麼?如何封裝?
- 隱藏“齒輪和控制桿”
- 只暴露客戶需要的職責
- 防止對象受到外界干擾
- 防止其它對象依賴可能變化的細節
- 信息隱藏有助於對象和模塊之間的鬆散耦合,使得設計更加靈活,更易於重用
- 減少代碼之間的依賴
- “有好籬笆纔有好鄰居”
- 例如:汽車的氣動踏板
- 最佳實踐:對象之間只通過方法(函數)互相訪問。切忌直接訪問屬性。
class Person {
public int age;
}
class BetterPerson {
private int age; // change to dateOfBirth
public int getAge() { return age; }
}
更完善的 Person 類可能是: private dateOfBirth
- 最佳實踐:對象之間只通過方法(函數)互相訪問。切忌直接訪問屬性。
- 抽象使得泛化(generalizaions)成爲可能
- 簡化問題-忽略複雜的細節
- 關注共性,並且允許變更
- 人類經常使用泛化。
- 當你看見約翰和簡家裏的那頭灰德國牧羊犬時,你有沒有......想到“狗”這個詞?
- 抽象同樣能簡化計算機程序。例如,軟件中有兩個重要抽象:客戶端和服務器(clients and servers)
- 在圖形用戶界面中,系統可能會詢問用戶各種問題:
- 是或不是
- 多選一
- 輸入數字
- 任意文本問題
- 統一處理這些問題會顯得很簡單,每一個問題都作爲Question類的特例(specialization);程序只需維護 這些問題的實例列表,分別調用各自的askTheUser()方法。
- 繼承用於描述一個類與其它類的不同之處。例如:類Y像類X,但有下列不同...
- 爲什麼使用繼承?
- 你有兩種類型,其中一種是另一種的擴展。
- 有時(不是所有時候)你想忽略對象之間的不同,而只關注它們的共同之處(基類)。這就是泛化。
- 假如某系統需要對不同的形狀進行操作(經典例子):
- 有時你並不關心你正在操作的形狀的種類(例如,移動形狀時)
- 有時你必須知道形狀的種類(在顯示器上繪製形狀)
- 以上來源自Andrew Koenig:
- www.cs.princeton.edu/courses/archive/spring98/cs333/lectures
- See /10/tsld001.htm, through tsld034.htm.
- 派生類繼承自基類;派生類擴展了基類;派生類是基類的特殊化(specialization)。
- 派生類能夠提供額外的狀態(數據成員),或額外的行爲(成員函數/方法),或覆蓋所繼承的方法。
- 基類是所有它的派生類的泛化。如:通常所有寵物都有名字。
- 基類(Base Class)=父類(parent class)=超類(superclass)
- 派生類(Derived Class)=子類(child class)=子類(subclass)
- 繼承含有(有些,不是全部)是一個(is-a)或是一種(is-a-kind-of)的關係
- 正方形是一種矩形(使用繼承)
- Leroy 是一種狗(不使用繼承)
- 傳統的過程分析和設計中不能很好地模擬這種關係
- 繼承是一種強有力的機制
- 使我們關注共性,而不是特定的細節
- 使得代碼可以重用且富有彈性(能適應變化)
-
實現繼承(Implementation inheritance):派生類繼承基類的屬性和行爲
-
接口繼承(Interface inheritance):類實現抽象接口的方法,保留既定語義(intended semantics)
- C++允許多重實現繼承
- Java規定派生類只能有一個基類,但可以繼承自多個接口
- 是一種允許多個類針對同一消息有不同的反應的能力
- 對於任何實現了給定接口的對象,在不明確指定類名的情況下,就可以使用。
- 例如:question.askTheUser();
- 當然,這些不同反應都有類似的本質
- 儘可能使用接口繼承和動態(運行期)綁定
-
Liskov 替換原則:如果Y是X的子類,那麼在任何使用X實例的地方都可以用Y的實例來替換。
// 下面的代碼將輸出什麼?
// Refer to the Beginning Java link on the course web site.
package question;
abstract class Question { // Full class name is question. QuestionTest
public Question( String _text ) { // Constructor
theText = _text;
}
public abstract void askTheUser();
protected String theText;
}
class YesNoQuestion extends Question {
public YesNoQuestion( String _text ) { super( _text ); }
public void askTheUser() {
System.out.println( theText );
System.out.println( "YES or NO ...?" );
}
}
class FreeTextQuestion extends Question {
public FreeTextQuestion( String _text ) { super( _text ); }
public void askTheUser() {
System.out.println( theText );
System.out.println( "Well...? What’s the answer...?" );
}
}
public class QuestionTest {
public static void main(String[] args) {
Question[] questions = getQuestions();
for (int i = 0; i < questions.length; i++) {
questions[i].askTheUser(); // Polymorphism !!!
}
}
private static Question[] getQuestions() {
Question[] qs = new Question[2];
qs[0] = new YesNoQuestion("Do you understand polymorphism?");
qs[1] = new FreeTextQuestion("Why is polymorphism good?");
return qs;
}
}
輸出:
Do you understand polymorphism?
YES or NO ...?
Why is polymorphism good?
Well...? What's the answer...?
// What will the following Java code output to the screen?
class Base {
final void foo() {
System.out.println("Base foo");
}
void bar() {
System.out.println("Base bar");
}
}
public class Derived extends Base {
void bar() {
System.out.println("Derived bar");
}
public static void main(String[] args) {
Derived d = new Derived();
d.foo();
d.bar();
Base b = d;
b.bar();
}
}
輸出:
Base foo
Derived bar
Derived bar
- 封裝:只暴露公有接口,隱藏了複雜的實現細節,避免代碼之間複雜的相互依賴
- 多態:允許有相同接口的類互相替換,由此減小代碼的複雜度
- 繼承:使用抽象類或接口實現泛化來減小複雜度
- 委託:通過從更小、封裝更好的服務來構建更完整或更高層次的服務來減小複雜度。委託還增加了運行時的靈活性。
- 我們主要使用名詞,然後對它進行修飾和增加屬性,最後,把它和動詞聯合在一起
- 面向對象設計遵循這個模式
- 過程化設計不遵循這個模式
- 這就是爲什麼人們經常發現對象更容易理解。
- 我們從對象模型中能形成構造良好的主謂賓(Subject-verb-object)句子:
- 人們擁有寵物。
- Paula 擁有 Leroy。
- 試試用功能分解來形成主謂賓句子!
而且,人們廣泛使用抽象和泛化...
- 首先出現機器語言
- 在此基礎上發展出彙編語言,提供了符號
- 高級語言出現:Fortran, Pascal, C等。它們提供了程序語句之間的“結構”關係。
- “goto”的使用日漸稀少,這有助於簡化程序結構。
- 數據結構和算法提供了程序結構的可重用模式,促進更高層次上的抽象。
- 面向對象的抽象是爲了關注於解決問題,而不是機器
- 通過更高層次的抽象,程序語句之間的關係轉化成爲相對簡單的對象協作關係
- 設計模式提供可重用的對象結構...
- 組件非常有用...代碼重用
- 設計模式很好...設計重用
- 接口不錯...靈活健壯的代碼
- 底層結構(infrastructure)和可重用服務同樣很好
- 接口能完美分離個人和團隊的職責,增加團隊效率
- 鬆散耦合和模塊化提高了擴展性、靈活性、可量測性和重用性
- 邏輯變化很自然的被隔離起來,這多虧了對象的模塊化和信息隱藏(封裝)。這意味着實現更快,維護更容易。
- 面向對象中間件使我們無須關注位置、平臺和語言
- 一組設計良好的對象是我們可以增加新功能而不用更改設計
- 藝術多於科學
- 可解決問題的模型本質上當然沒有問題,但是一些模型就是比其它的好,這是因爲它們更實用、更靈活、更容易擴展、更方便理解、更簡單...
- 第一個設計幾乎不可能是最好的設計。找到最好的抽象來對問題建模始終不是一件容易的事情,經驗很重要。
- 經常需要嘗試多次,來確定如何劃分系統各部分之間的邊界纔是最好?每一部分應該爲其它部分提供什麼接口?
- 以體系結構爲中心,而不是功能爲中心。首先關注全面的大體的,其次纔是具體的特定的。設計時首先做到這一點,就成功了一大半。
- 設計中要考慮可能發生的擴展,使得以後擴展是遞增式的,不用更改設計。不同的設計可以有完全相同的功能,但是在這方面可能完全不同。
- 儘量推廣可重用的面向服務的底層結構,這樣,在需求不可避免的變化時,代碼也能更快、更容易的更改。
- 是面向對象分析中採用的最普遍的方法
- 基於“客戶端/服務器”關係
- 對“客戶端/服務器”有兩種通用的解釋:
- 用於分佈式體系中,服務器提供對共享資源(如數據庫)的訪問,客戶端提供用戶界面
- 用於面向對象術語中,服務器是一個提供服務的對象;在這裏我們使用這個含義
- 客戶端與服務器協作(發送消息)
- 一個對象可能在一個協作中是客戶端,而在另一個協作中是服務器
- 服務器負責提供某種服務
- 一般來說,對象應該以某種定義良好的方式工作
- 查看領域,識別對象、類
- 通常首先識別出對象
- 通過對象分組找到類
- 確定對象之間和類之間的關係
- 結構關係
- 協作關係
- 賦予職責
- 基於協作關係
- 迭代,迭代,迭代,迭代,迭代,迭代...
- 以領域建模作爲開始,而不是以建模解決方案作爲開始
- CRC方法使用3×5(英寸)索引卡片,一個類就是一張卡片,卡片上寫有該類的職責以及爲了完成這些職責必須與哪些類協作。類的簡要描述寫在卡片背面。
- 下面的例子中,類Foo必須與類X和類Y協作(給它們發送消息),以完成“do something”責任。
1: |
2: | |
3: | | |
4: | | | |
- 玩家輪流參加,每人可以從任何一個非空行中移走一根或多根棍子。移走最後一根棍子的人爲輸家。
- 遊戲開始時,程序將顯示遊戲的狀態:輪到誰了,還有幾行,還有多少棍子。
- 操作不符合規則,程序將給出提示。(如所移走的棍子數目超過該行的棍子總數)
- 找到對象和類…
- 用CRC卡片
- 附加問題:哪個類負責記錄輪到那個玩家了?
- 類(Class)
- – 抽象(Abstract) / 具體(Concrete) / 元(Meta)
- 對象(Object)
- – 實例(Instance)
- – 標識(Identity)
- 屬性(Attribute)
- – 成員(Member)
- – 域(Field)
- – 狀態(State)
- 行爲(Behavior)
- – 方法(Method)
- – 成員函數(Member Function)
- – 操作(Operation)
- – 職責(Responsibility)
- – 消息(Message)
- – 調用方法(Method Call)
- 接口(Interface)
- 抽象(Abstraction)
- – 泛化(Generalization)
- – 特殊化(Specialization)
- 繼承(Inheritance)
- – 接口(Interface) / 實現(Implementation)
- – 基類(Base) / 派生類(Derived), 父類(Parent) / 子類(Child), 超類(Super) / 子類(Sub)
- 委託(Delegation)
- 協作(Collaboration)
- 多態(Polymorphism)
- – Liskov 替換原則(Liskov Substitution Principle)
- – 動態綁定(Dynamic (run-time) Binding)
- 聚合(Aggregation)
- 底層結構(Infrastructure)
- – 服務(Services) / 中間件(Middleware) / 框架(Frameworks)
- 統一建模語言(Unified Modeling Language (UML))
- 分析(Analysis) / 設計(Design) / 實現(Implementation) / 架構(Architecture) / 過程(Process)
- 鬆散耦合(Loose Coupling & Flexibility)
- 封裝(Encapsulation)
- – 信息隱藏(Information Hiding)
- 模塊性(Modularity)
- 透明(Transparency)
- Java
- – 構造函數(Constructor) / 包(Package) / 靜態(Static)
- 模式(Patterns)
- 職責驅動設計(Responsibility driven design)