摘要:代碼使用泛型類中不依賴于類型參數的方法。委托依賴于動態綁定,因為它要求給定的方法調用可以在運行時調用不同的代碼段。委托捕獲操作并將其發送給另一個對象。委托可以被看作是在對象層次上的復用機制,而繼承是類層次上的復用機制。
大綱
設計可復用的類
繼承和重寫
重載(Overloading)
參數多態和泛型編程
行為子類型與Liskov替換原則
組合與委托
設計可復用庫與框架
API和庫 - 框架
Java集合框架(一個例子)
設計可復用的類在OOP中設計可復用的類
封裝和信息隱藏
繼承和重寫
多態性,子類型和重載
泛型編程
行為子類型和Liskov替代原則(LSP)
組合與委托
行為子類型
子類型多態性:客戶端代碼可以統一處理不同種類的對象。 子類型多態:客戶端可用統一的方式處理不同類型的對象
如果Cat的類型是Animal的一個子類型,那么只要使用Animal類型的表達式,就可以使用Cat類型的表達式。
假設q(x)是T類型對象x可證明的性質,那么對于S類型的對象y,q(y)應該是可證明的,其中S是T的一個子類型。 - Barbara Liskov
Java編譯器執行的規則(靜態類型檢查)
子類可以添加,但不能刪除方法
具體類必須實現所有未定義的方法
重寫方法必須返回相同的類型或子類型
重寫方法必須接受相同的參數類型
重寫方法不會拋出額外的異常
也適用于指定的行為(方法):
相同或更強的不變量
相同或較弱的先決條件
相同或更強的后置條件
Liskov替代原則(LSP)
LSP是一種特定的子類型關系定義,稱為強行為子類型化
在編程語言中,LSP依賴于以下限制:
先決條件不能在子類型中加強。前置條件不能強化
后置條件在子類型中不能被削弱。后置條件不能弱化
超類型的不變式必須保存在一個子類型中。不變量要保持
子類型中方法參數的變換。子類型方法參數:逆變
子類型中返回類型的協邊。子類型方法的返回值:協變
子類型的方法不應引發新的異常,除非這些異常本身是超類型方法拋出的異常的子類型。 異常類型:協變(這將在第7-2節討論)
Covariance (協變)
父類型到子類型:
越來越具體specific
返回值類型:不變或變得更具體
異常的類型:也是如此
Contravariance (反協變、逆變)
父類型到子類型:
越來越具體specific
參數類型:要相反的變化,要不變或越來越抽象
從邏輯上講,它被稱為子類型中方法參數的逆變。
這在Java中實際上是不允許的,因為它會使重載規則復雜化。
協變和反協變
數組是協變的:根據Java的子類型規則,T []類型的數組可能包含T類型的元素或T的任何子類型。
在運行時,Java知道這個數組實際上是作為一個整數數組實例化的,它只是簡單地通過Number []類型的引用來訪問。
區分:對象的類型與引用的類型
考慮泛型中的LSP
泛型是類型不變的
ArrayList
List
編譯完成后,編譯器會丟棄類型參數的類型信息; 因此這種類型的信息在運行時不可用。
這個過程被稱為類型擦除
泛型不是協變的。
什么是類型擦除?
類型擦除:如果類型參數是無界的,則將泛型類型中的所有類型參數替換為它們的邊界或對象。 因此,生成的字節碼只包含普通的類,接口和方法。
泛型中的通配符
無界通配符類型使用通配符(?)指定,例如List <?>。
這被稱為未知類型的列表。
有兩種情況,無界通配符是一種有用的方法:
如果您正在編寫可以使用Object類中提供的功能實現的方法。
代碼使用泛型類中不依賴于類型參數的方法。 例如,List.size或List.clear。 事實上,Class <?>經常被使用,因為Class
下限通配符: super A>
上限通配符: extends A>
考慮具有通配符的泛型的LSP
List
List
List
Interface Comparator
int compare(T o1,T o2):比較它的兩個參數的順序。
一個比較函數,它對某些對象集合進行總排序。
可以將比較器傳遞給排序方法(如Collections.sort或Arrays.sort),以便精確控制排序順序。 比較器也可以用來控制某些數據結構(例如排序集合或排序映射)的順序,或者為沒有自然排序的對象集合提供排序。
如果你的ADT需要比較大小,或者要放入Collections或Arrays進行排序,可實現Comparator接口并重寫compare()函數。
該接口對每個實現它的類的對象進行總排序。
這種順序被稱為類的自然順序,類的compareTo方法被稱為其自然比較方法。
另一種方法:讓你的ADT實現Comparable接口,然后重寫compareTo()方法
與使用Comparator的區別:不需要構建新的Comparator類,比較代碼放在ADT內部。
委托
委托只是當一個對象依賴另一個對象來實現其功能的某個子集時(一個實體將某個事物傳遞給另一個實體)
委派/委托:一個對象請求另一個對象的功能
例如分揀機正在委托比較器的功能
委派是復用的一種常見形式
分揀機可以重復使用任意的排序順序
比較器可以重復使用需要比較整數的任意客戶端代碼
委托可以被描述為在實體之間共享代碼和數據的低級機制。
顯式委托:將發送對象傳遞給接收對象
隱式委托:由語言的成員查找規則
委托模式是實施委托的一種軟件設計模式,雖然這個術語也用于松散地進行咨詢或轉發。
委托依賴于動態綁定,因為它要求給定的方法調用可以在運行時調用不同的代碼段。
處理
接收者對象將操作委托給Delegate對象
接收者對象確保客戶端不會濫用委托對象。
委托與繼承
繼承:通過新操作擴展基類或重寫操作。
委托:捕獲操作并將其發送給另一個對象。
許多設計模式使用繼承和委派的組合。
將繼承替換為委派
問題:你有一個只使用其超類的一部分方法的子類(或者它不可能繼承超類數據)。
解決方案:創建一個字段并在其中放入一個超類對象,將方法委托給超類對象,并消除繼承。
實質上,這種重構拆分了兩個類,并使超類成為子類的幫助者,而不是其父類。
代替繼承所有的超類方法,子類將只有必要的方法來委派給超類對象的方法。
一個類不包含從超類繼承的任何不需要的方法。
合成繼承原則
或稱為合成復用原則(CRP)
類應該通過它們的組合(通過包含實現所需功能的其他類的實例)實現多態行為和代碼復用,而不是從基類或父類繼承。
最好組合一個對象可以做的事(has_a)而不是擴展它(is_a)。
委托可以被看作是在對象層次上的復用機制,而繼承是類層次上的復用機制。
“委托”發生在objet層面,而“繼承”發生在類層面
合成繼承原則
組合繼承的實現通常始于創建代表系統必須展現的行為的各種接口。
實現已識別的接口的類將根據需要構建并添加到業務域類中。
這樣,系統行為就沒有繼承地實現了。
使用接口定義不同側面的行為
接口之間通過擴展實現行為的擴展(接口組合)
類實現組合接口
委托的類型
使用(A使用B)
組合/聚合(A擁有B)
關聯(A有B)
這種分類是根據被委托者和委托者之間的“耦合程度”。
(1)依賴:臨時性的委托
使用類的最簡單形式是調用它的方法;
這兩種類別之間的關系形式被稱為“uses-a”關系,其中一個類使用另一個類而不實際地將其作為屬性。 例如,它可能是一個參數或在方法中本地使用。
依賴關系:對象需要其他對象(供應商)實施的臨時關系。
(2)關聯:永久性的委托
關聯:對象類之間的一種持久關系,它允許一個對象實例使另一個對象代表它執行一個動作。
has_a:一個類有另一個作為屬性/實例變量
這種關系是結構性的,因為它指定一種對象與另一種對象相連,并不代表行為。
(3)組成:更強的委托
組合是一種將簡單對象或數據類型組合成更復雜的對象的方法。
is_part_of:一個類有另一個作為屬性/實例變量
實現了一個對象包含另一個對象。
(4)聚合
聚合:對象存在于另一個之外,在外部創建,所以它作為參數傳遞給構造者。
has_a
組合(Composition)與聚合(Aggregation)
在組合中,當擁有的對象被破壞時,被包含的對象也被破壞。
一所大學擁有多個部門,每個部門都有一批教授。 如果大學關閉,部門將不復存在,但這些部門的教授將繼續存在。
在聚合中,這不一定是正確的。
大學可以被看作是一個部門的組合,而部門則擁有一批教授。 一位教授可以在一個以上的部門工作,但一個部門不能成為多個大學的一部分。
設計系統級可復用的庫和框架實際中的庫和框架
定義關鍵抽象及其接口
定義對象交互和不變量
定義控制流程
提供體系結構指導
提供默認值
之所以庫和框架被稱為系統層面的復用,是因為它們不僅定義了1個可復用的接口/類,而是將某個完整系統中的所有可復用的接口/類都實現出來,并且定義了這些類之間的交互關系,調用關系,從而形成了系統整體的“架構”。
更多條款
API:應用程序編程接口,庫或框架的接口
客戶端:使用API的代碼
插件:定制框架的客戶端代碼
擴展點:框架內預留的“空白”,開發者開發出符合接口要求的代碼(即插件),框架可調用,從而相當于開發者擴展了框架的功能
協議:API和客戶端之間預期的交互順序
回調:框架調用來訪問定制功能的插件方法
生命周期方法:根據協議和插件狀態按順序調用的回調方法
為什么API設計很重要?
如果你編程,你是一個API設計師,并且API可以是你最大的資產之一
好的代碼是模塊化的
每個模塊都有一個API
用戶大量投資:收購,寫作,學習
根據API思考改進代碼質量
成功的公共API捕捉用戶
也可以是你最大的責任
糟糕的API可能會導致無盡的支持調用流
可以抑制前進的能力
公共API是永遠的
有一個機會讓它正確
一旦模塊擁有用戶,就不能隨意更改API
(1)API應該做一件事,做得好
功能應該很容易解釋
如果名稱很難,那通常是一個不好的跡象
好名字推動發展
適合分解和合并模塊
(2)API應該盡可能小,但不能更小
API應該滿足其要求
功能,類別,方法,參數等
你可以隨時添加,但你永遠不能刪除
尋找一個很好的功率重量比
(3)實施不應該影響API
API中的實施細節是有害的
迷惑用戶
禁止改變執行的自由
請注意什么是實施細節
不要過分指定方法的行為
例如:不要指定散列函數
所有調整參數都是可疑的
不要讓實現細節“泄露”到API中
序列化表單,拋出異常
盡量減少一切的可達性(信息隱藏)
讓班級成員盡可能私人化
公共班級不應該有公共領域
(4)文件事宜
記錄每個類,接口,方法,構造函數,參數和異常
類:什么是實例
方法:方法和客戶之間的契約
先決條件,后置條件,副作用
參數:指示單位,表格,所有權
文件線程安全
如果類是可變的,則記錄狀態空間
重復使用比說要容易得多。 這樣做需要良好的設計和非常好的文檔。 即使我們看到良好的設計(這仍然不常見),如果沒有良好的文檔,我們也不會看到組件被復用。 - D. L. Parnas軟件老化,ICSE 1994
(5)考慮績效后果
不好的決定會限制性能
使類型變化
提供構造函數而不是靜態工廠
使用實現類型而不是接口
不要扭曲API來獲得性能
潛在的性能問題將得到解決,但頭痛將永遠伴隨著你
良好的設計通常與良好的性能相吻合
糟糕的API決策的性能影響可能是真實且永久的
Component.getSize()返回Dimension,但Dimension是可變的,因此每個getSize調用都必須分配Dimension,導致數百萬無用的對象分配
(6)API必須與平臺和平共存
習慣做什么
遵守標準的命名約定
避免過時的參數和返回類型
模仿核心API和語言中的模式
利用API友好功能
泛型,可變參數,枚舉,函數接口
了解并避免API陷阱和陷阱
終結器,公共靜態最終數組等。
不要音譯API
(7)類設計
最小化可變性:除非有充分的理由否則類應該是不可變的
優點:簡單,線程安全,可重復使用
缺點:為每個值分開對象
如果可變,保持狀態空間小,定義明確。
只有子類才有意義:子類化會影響替代性(LSP)
除非存在某種關系,否則不要繼承。 否則,請使用委托或組合。
不要為了復用實現而繼承子類。
繼承違反封裝,子類對超類的實現細節很敏感
(8)方法設計
不要讓客戶做任何模塊可以做的事情
客戶通常通過剪切和粘貼,這是丑陋的,煩人的,錯誤的。
API應該快速失?。罕M快報告錯誤。 編譯時間最好 - 靜態類型,泛型。
在運行時,第一個錯誤的方法調用是最好的
方法應該是失敗原子的
以字符串形式提供對所有可用數據的編程訪問。 否則,客戶端會解析字符串,這對客戶來說很痛苦
過度謹慎。 通常最好使用不同的名稱。
使用適當的參數和返回類型。
歡迎界面類型的類輸入靈活性,性能
使用最具體的可能輸入參數類型,從而將錯誤從運行時移到編譯時間。
避免長參數列表。 三個或更少的參數是理想的。
如果你必須使用很多參數呢?
避免需要特殊處理的返回值。 返回零長度數組或空集合,不為null。
(2)框架設計白盒和黑盒框架
白盒框架
通過繼承和覆蓋方法進行擴展
通用設計模式:模板方法
子類具有主要方法,但對框架進行控制
黑盒框架
通過實現插件接口進行擴展
通用設計模式:策略,觀察者
插件加載機制加載插件并對框架進行控制
白盒與黑盒框架
白盒框架使用子類/子類型---繼承
允許擴展每個非私有方法
需要了解超類的實現
一次只能有一個分機
匯編在一起
通常所謂的開發者框架
黑盒框架使用組合 - 委派/組合
允許擴展在界面中顯示的功能
只需要了解接口
多個插件
通常提供更多的模塊化
可以多帶帶部署(.jar,.dll,...)
通常稱為最終用戶框架,平臺
框架設計考慮
一旦設計好,改變的機會就很小
關鍵決策:將通用部件與可變部件分開
你想解決什么問題?
可能的問題:
擴展點太少:限于狹窄的用戶類別
延伸點過多:難以學習,速度緩慢
太通用:很少復用價值
“最大限度地利用重復使用最小化”
典型的框架設計和實現
定義你的域名
識別潛在的公共部分和可變部分
設計和編寫示例插件/應用程序
分解和實施通用部件為框架
為可變部分提供插件接口和回調機制
在適當的地方使用眾所周知的設計原則和模式...
獲得大量的反饋,并迭代
這通常被稱為“域工程”。
進化設計:提取共同點
提取界面是進化設計中的一個新步驟:
抽象類是從具體類中發現的
接口是從抽象類中提取的
一旦架構穩定就開始
從課堂上刪除非公開的方法
將默認實現移動到實現接口的抽象類中
運行一個框架
一些框架可以自行運行
例如 Eclipse
其他框架必須擴展才能運行
Swing,JUnit,MapReduce,Servlets
加載插件的方法:
客戶端寫入main(),創建一個插件并將其傳遞給框架
Framework寫入main(),客戶端將plugin的名稱作為命令行參數或環境變量傳遞
Framework在一個神奇的位置查找,然后配置文件或.jar文件被自動加載和處理。
(3)Java集合框架什么是收集和收集框架?
集合:對元素進行分組的對象
主要用途:數據存儲和檢索,以及數據傳輸
熟悉的例子:java.util.Vector,java.util.Hashtable,Array
集合框架:一個統一的架構
接口 - 實現獨立
實現 - 可復用的數據結構
算法 - 可復用的功能
最著名的例子
C++標準模板庫(STL)
Java集合框架(JCF)
同步包裝(不是線程安全的!)
同步包裝:線程安全的新方法
匿名實現,每個核心接口一個
靜態工廠需要收集適當的類型
如果通過包裝進行全部訪問,線程安全保證
必須手動同步迭代
那時是新的; 現在已經老了!
同步包裝很大程度上已經過時
由同時收集而過時
總結設計可復用的類
繼承和重寫
超載
參數多態和泛型編程
行為分類和Liskov替代原則(LSP)
組成和委派
設計系統級可復用的庫和框架
API和庫
框架
Java集合框架(一個例子)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71349.html
摘要:大綱什么是軟件復用如何衡量可復用性可復用組件的級別和形態源代碼級別復用模塊級別的復用類抽象類接口庫級別的復用包系統級別的復用框架對可復用性的外部觀察類型變化例行分組實施變更代表獨立分解常見行為總結什么是軟件復用軟件復用軟件復用是使用現有軟件 大綱 什么是軟件復用?如何衡量可復用性?可復用組件的級別和形態 源代碼級別復用 模塊級別的復用:類/抽象類/接口 庫級別的復用:API /包 系...
摘要:共性的步驟在抽象類內公共實現,差異化的步驟在各個子類中實現子類為每個步驟提供不同的實現。模板方法將算法的骨架定義為抽象類,允許其子類提供具體行為。迭代器依次訪問對象的元素而不暴露其基礎表示。 大綱 結構模式 Adapter允許具有不兼容接口的類通過將自己的接口包裝到已有類的接口中來一起工作。 Decorator動態添加/覆蓋對象的現有方法中的行為。 Facade為大量代碼提供簡化的界...
摘要:設計方案的容易改變這就是所謂的軟件構建的可維護性,可擴展性和靈活性。這也可能表明類型或方法可能難以維護?;谠创a中不同運算符和操作數的數量的合成度量。對修改的封閉這種模塊的源代碼是不可侵犯的。 大綱 軟件維護和演變可維護性度量模塊化設計和模塊化原則OO設計原則:SOLIDOO設計原則:GRASP總結 軟件維護和演變 什么是軟件維護? 軟件工程中的軟件維護是交付后修改軟件產品以糾正故障...
摘要:抽象工廠模式將具有共同主題的對象工廠分組。對可重用性和可維護性設計模式的高層考慮創造性模式工廠方法模式也稱為虛擬構造器意圖定義一個用于創建對象的接口,但讓子類決定實例化哪個類。 大綱 創造性模式 工廠方法模式創建對象而不指定要創建的確切類。 抽象工廠模式將具有共同主題的對象工廠分組。 Builder模式通過分離構造和表示來構造復雜的對象。 結構模式 Bridge將抽象從其實現中分...
摘要:什么事面向對象面向對象方法是一種運用對象,類,繼承,封裝,聚合,關聯,消息,多態等概念和原則來構造軟件系統的開發思想方法。面向對象方法以眾多的類及交互模式類間的協同工作為中心。 1.什么事面向對象? 面向對象方法是一種運用對象,類,繼承,封裝,聚合,關聯,消息,多態等概念和原則來構造軟件系統的開發思想(方法)。 2.面向對象中的的基本概念: A.對象:把問題域中的事物抽象地表示為系統中...
閱讀 1017·2023-04-25 22:27
閱讀 872·2021-11-22 14:56
閱讀 984·2021-11-11 16:54
閱讀 1678·2019-08-30 15:54
閱讀 3500·2019-08-30 13:20
閱讀 1213·2019-08-30 10:55
閱讀 2080·2019-08-26 13:34
閱讀 3281·2019-08-26 11:53