国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

一些面向?qū)ο蟮脑O(shè)計(jì)法則

2019-11-18 15:19:05
字體:
供稿:網(wǎng)友

  法則1:優(yōu)先使用(對象)組合,而非(類)繼續(xù)
[ Favor Composition Over Inheritance ]
組合
(對象)組合是一種通過創(chuàng)建一個(gè)組合了其它對象的對象,從而獲得新功能的復(fù)用方法。
將功能委托給所組合的一個(gè)對象,從而獲得新功能。
有些時(shí)候也稱之為“聚合”(aggregation)或“包容”(containment),盡管有些作者對這些術(shù)語賦予了專門的含義
例如:
聚合:一個(gè)對象擁有另一個(gè)對象或?qū)α硪粋€(gè)對象負(fù)責(zé)(即一個(gè)對象包含另一個(gè)對象或是另一個(gè)對象的一部分),并且聚合對象和其所有者具有相同的生命周期。(譯者注:即所謂的“同生共死”關(guān)系,可參見GOF的Design Patterns: Elements of Reusable Object-Oriented Software的引言部分。)
包容:一種非凡類型的組合,對于其它對象而言,容器中的被包含對象是不可見的,其它對象僅能通過容器對象來訪問被包含對象。(Coad)

包含可以通過以下兩種方式實(shí)現(xiàn):
根據(jù)引用(By reference)
根據(jù)值(By value)
C++答應(yīng)根據(jù)值或引用來實(shí)現(xiàn)包含。
但是在java中,一切皆為對象的引用!
組合的優(yōu)點(diǎn)和缺點(diǎn)
優(yōu)點(diǎn):
容器類僅能通過被包含對象的接口來對其進(jìn)行訪問。
“黑盒”復(fù)用,因?yàn)楸话瑢ο蟮膬?nèi)部細(xì)節(jié)對外是不可見。
對裝性好。
實(shí)現(xiàn)上的相互依靠性比較小。(譯者注:被包含對象與容器對象之間的依靠關(guān)系比較少)
每一個(gè)類只專注于一項(xiàng)任務(wù)。
通過獲取指向其它的具有相同類型的對象引用,可以在運(yùn)行期間動(dòng)態(tài)地定義(對象的)組合。

缺點(diǎn):
從而導(dǎo)致系統(tǒng)中的對象過多。
為了能將多個(gè)不同的對象作為組合塊(composition block)來使用,必須仔細(xì)地對接口進(jìn)行定義。
繼續(xù)
(類)繼續(xù)是一種通過擴(kuò)展一個(gè)已有對象的實(shí)現(xiàn),從而獲得新功能的復(fù)用方法。
泛化類(超類)可以顯式地捕捉那些公共的屬性和方法。
非凡類(子類)則通過附加屬性和方法來進(jìn)行實(shí)現(xiàn)的擴(kuò)展。
繼續(xù)的優(yōu)點(diǎn)和缺點(diǎn)
優(yōu)點(diǎn):
輕易進(jìn)行新的實(shí)現(xiàn),因?yàn)槠浯蠖鄶?shù)可繼續(xù)而來。
易于修改或擴(kuò)展那些被復(fù)用的實(shí)現(xiàn)。
缺點(diǎn):
破壞了封裝性,因?yàn)檫@會(huì)將父類的實(shí)現(xiàn)細(xì)節(jié)暴露給子類。
“白盒”復(fù)用,因?yàn)楦割惖膬?nèi)部細(xì)節(jié)對于子類而言通常是可見的。
當(dāng)父類的實(shí)現(xiàn)更改時(shí),子類也不得不會(huì)隨之更改。
從父類繼續(xù)來的實(shí)現(xiàn)將不能在運(yùn)行期間進(jìn)行改變。
Coad規(guī)則
僅當(dāng)下列的所有標(biāo)準(zhǔn)被滿足時(shí),方可使用繼續(xù):
子類表達(dá)了“是一個(gè)…的非凡類型”,而非“是一個(gè)由…所扮演的角色”。
子類的一個(gè)實(shí)例永遠(yuǎn)不需要轉(zhuǎn)化(transmute)為其它類的一個(gè)對象。
子類是對其父類的職責(zé)(responsibility)進(jìn)行擴(kuò)展,而非重寫或廢除(nullify)。
子類沒有對那些僅作為一個(gè)工具類(utility class)的功能進(jìn)行擴(kuò)展。
對于一個(gè)位于實(shí)際的問題域(PRoblem Domain)的類而言,其子類特指一種角色(role),交易(transaction)或設(shè)備(device)。
繼續(xù)/組合示例1

“是一個(gè)…的非凡類型”,而非“是一個(gè)由…所扮演的角色”
失敗。乘客是人所扮演的一種角色。代理人亦然。
永遠(yuǎn)不需要轉(zhuǎn)化
失敗。隨著時(shí)間的發(fā)展,一個(gè)Person的子類實(shí)例可能會(huì)從Passenger轉(zhuǎn)變成Agent,再到Agent Passenger。
擴(kuò)展,而非重寫和廢除
通過。
不要擴(kuò)展一個(gè)工具類
通過。
在問題域內(nèi),特指一種角色,交易或設(shè)備
失敗。Person不是一種角色,交易或設(shè)備。
繼續(xù)并非適用于此處!

使用組合進(jìn)行拯救!

繼續(xù)/組合示例2

“是一個(gè)…的非凡類型”,而非“是一個(gè)由…所扮演的角色” 通過。乘
客和代理人都是非凡類型的人所扮演的角色。
n 永遠(yuǎn)不需要轉(zhuǎn)化
通過。一個(gè)Passenger對象將保持不變;Agent對象亦然。
n 擴(kuò)展,而非重寫和廢除
F 通過。
n 不要擴(kuò)展一個(gè)工具類
F 通過。
n 在問題域內(nèi),特指一種角色,交易或設(shè)備
F 通過。PersonRole是一種類型的角色。
繼續(xù)適用于此處!
繼續(xù)/組合示例3

n “是一個(gè)…的非凡類型”,而非“是一個(gè)由…所扮演的角色”
F 通過。預(yù)訂和購買都是一種非凡類型的交易。
n 永遠(yuǎn)不需要轉(zhuǎn)化
F 通過。一個(gè)Reservation對象將保持不變;Purchase對象亦然。
n 擴(kuò)展,而非重寫和廢除
F 通過。
n 不要擴(kuò)展一個(gè)工具類
F 通過。
n 在問題域內(nèi),特指一種角色,交易或設(shè)備
F 通過。是一種交易。
繼續(xù)適用于此處!
繼續(xù)/組合示例4

n “是一個(gè)…的非凡類型”,而非“是一個(gè)由…所扮演的角色”
F 失敗。預(yù)訂不是一種非凡類型的observable。
n 永遠(yuǎn)不需要轉(zhuǎn)化
F 通過。一個(gè)Reservation對象將保持不變。
n 擴(kuò)展,而非重寫和廢除
F 通過。
n 不要擴(kuò)展一個(gè)工具類
F 失敗。Observable就是一個(gè)工具類。
n 在問題域內(nèi),特指一種角色,交易或設(shè)備
F 不適用。Observable是一個(gè)工具類,并非一個(gè)問題域的類。。
繼續(xù)并非適用于此處!
繼續(xù)/組合總結(jié)
n 組合與繼續(xù)都是重要的重用方法
n 在OO開發(fā)的早期,繼續(xù)被過度地使用
n 隨著時(shí)間的發(fā)展,我們發(fā)現(xiàn)優(yōu)先使用組合可以獲得重用性與簡單性更佳的設(shè)計(jì)
n 當(dāng)然可以通過繼續(xù),以擴(kuò)充(enlarge)可用的組合類集(the set of composable classes)。
n 因此組合與繼續(xù)可以一起工作
n 但是我們的基本法則是:
優(yōu)先使用對象組合,而非(類)繼續(xù)
[ Favor Composition Over Inheritance ]
法則2:針對接口編程,而非(接口的)實(shí)現(xiàn)
[ Program To An Interface, Not An Implementation ]
接口
n 接口是一個(gè)對象在對其它的對象進(jìn)行調(diào)用時(shí)所知道的方法集合。
n 一個(gè)對象可以有多個(gè)接口(實(shí)際上,接口是對象所有方法的一個(gè)子集)
n 類型是對象的一個(gè)特定的接口。
n 不同的對象可以具有相同的類型,而且一個(gè)對象可以具有多個(gè)不同的類型。
n 一個(gè)對象僅能通過其接口才會(huì)被其它對象所了解。
n 某種意義上,接口是以一種非常局限的方式,將“是一種…”表達(dá)為“一種支持該接口的…”。
n 接口是實(shí)現(xiàn)插件化(pluggability)的要害
實(shí)現(xiàn)繼續(xù)和接口繼續(xù)
n 實(shí)現(xiàn)繼續(xù)(類繼續(xù)):一個(gè)對象的實(shí)現(xiàn)是根據(jù)另一個(gè)對象的實(shí)現(xiàn)來定義的。
n 接口繼續(xù)(子類型化):描述了一個(gè)對象可在什么時(shí)候被用來替代另一個(gè)對象。
n C++的繼續(xù)機(jī)制既指類繼續(xù),又指接口繼續(xù)。
n C++通過繼續(xù)純虛類來實(shí)現(xiàn)接口繼續(xù)。
n Java對接口繼續(xù)具有單獨(dú)的語言構(gòu)造方式-Java接口。
n Java接口構(gòu)造方式更加易于表達(dá)和實(shí)現(xiàn)那些專注于對象接口的設(shè)計(jì)。
接口的好處
n 優(yōu)點(diǎn):
F Client不必知道其使用對象的具體所屬類。
F 一個(gè)對象可以很輕易地被(實(shí)現(xiàn)了相同接口的)的另一個(gè)對象所替換。
F 對象間的連接不必硬綁定(hardwire)到一個(gè)具體類的對象上,因此增加了靈活性。
F 松散藕合(loosens coupling)。
F 增加了重用的可能性。
F 提高了(對象)組合的機(jī)率,因?yàn)楸话瑢ο罂梢允侨魏螌?shí)現(xiàn)了一個(gè)指定接口的類。
n 缺點(diǎn):
F 設(shè)計(jì)的復(fù)雜性略有增加
(譯者注:接口表示“…像…”(LikeA)的關(guān)系,繼續(xù)表示“…是…”(IsA)的關(guān)系,組合表示“…有…”(HasA)的關(guān)系。)
接口實(shí)例


n 該方法是指其它的一些類可以進(jìn)行交通工具的駕駛,而不必關(guān)心其實(shí)際上是(汽車,輪船,潛艇或是其它任何實(shí)現(xiàn)了IManeuverabre的對象)。
法則3:開放-封閉法則(OCP)
軟件組成實(shí)體應(yīng)該是可擴(kuò)展的,但是不可修改的。
[ Software Entities Should Be Open For Extension, Yet Closed For Modification ]
開放-封閉法則
n 開放-封閉法則認(rèn)為我們應(yīng)該試圖去設(shè)計(jì)出永遠(yuǎn)也不需要改變的模塊。
n 我們可以添加新代碼來擴(kuò)展系統(tǒng)的行為。我們不能對已有的代碼進(jìn)行修改。
n 符合OCP的模塊需滿足兩個(gè)標(biāo)準(zhǔn):
F 可擴(kuò)展,即“對擴(kuò)展是開放的”(Open For Extension)-模塊的行為可以被擴(kuò)展,以需要滿足新的需求。
F 不可更改,即“對更改是封閉的”(Closed for Modification)-模塊的源代碼是不答應(yīng)進(jìn)行改動(dòng)的。
n 我們能如何去做呢?
F 抽象(Abstraction)
F 多態(tài)(Polymorphism)
F 繼續(xù)(Inheritance)
F 接口(Interface)

n 一個(gè)軟件系統(tǒng)的所有模塊不可能都滿足OCP,但是我們應(yīng)該努力最小化這些不滿足OCP的模塊數(shù)量。
n 開放-封閉法則是OO設(shè)計(jì)的真正核心。
n 符合該法則便意味著最高等級的復(fù)用性(reusability)和可維護(hù)性(maintainability)。
OCP示例
n 考慮下面某類的方法:

n 以上函數(shù)的工作是在制訂的部件數(shù)組中計(jì)算各個(gè)部件價(jià)格的總和。
n 若Part是一個(gè)基類或接口且使用了多態(tài),則該類可很輕易地來適應(yīng)新類型的部件,而不必對其進(jìn)行修改。
n 其將符合OCP

n 但是在計(jì)算總價(jià)格時(shí),若財(cái)務(wù)部頒布主板和內(nèi)存應(yīng)使用額外費(fèi)用,則將如何去做。
n 下列的代碼是如何來做的呢?


n 這符合OCP嗎?
n 當(dāng)每次財(cái)務(wù)部提出新的計(jì)價(jià)策略,我們都不得不要修改totalPrice()方法!這并非“對更改是封閉的”。顯然,策略的變更便意味著我們不得不要在一些地方修改代碼的,因此我們該如何去做呢?
n 為了使用我們第一個(gè)版本的totalPrice(),我們可以將計(jì)價(jià)策略合并到Part的getPrice()方法中。

n 這里是Part和ConcretePart類的示例:


n 但是現(xiàn)在每當(dāng)計(jì)價(jià)策略發(fā)生改變,我們就必須修改Part的每個(gè)子類!
n 一個(gè)更好的思路是采用一個(gè)PricePolicy類,通過對其進(jìn)行繼續(xù)以提供不同的計(jì)價(jià)策略:




n 看起來我們所做的就是將問題推遲到另一個(gè)類中。但是使用該解決方案,我們可通過改變Part對象,在運(yùn)行期間動(dòng)態(tài)地來設(shè)定計(jì)價(jià)的策略。
n 另一個(gè)解決方案是使每個(gè)ConcretePart從數(shù)據(jù)庫或?qū)傩晕募蝎@取其當(dāng)前的價(jià)格。
單選法則
單選法則(the Single Choice Principle)是OCP的一個(gè)推論。
單選法則:
無論在什么時(shí)候,一個(gè)軟件系統(tǒng)必須支持一組備選項(xiàng),理想情況下,在系統(tǒng)中只能有一個(gè)類能夠知道整個(gè)的備選項(xiàng)集合。
法則4:Liskov替換法則(LSP)
使用指向基類(超類)的引用的函數(shù),必須能夠在不知道具體派生類(子類)對象類型的情況下使用它們。
[ Function Thar Use Referennces To Base(Super) Classes Must Be Able To Use Objects Of Derived(Sub) Classes Without Knowing It ]
Liskov替換法則
n 顯而易見,Liskov替換法則(LSP)是根據(jù)我所熟知的“多態(tài)”而得出的。
n 例如:

n 方法drawShape應(yīng)該可與Sharp超類的任何子類一起工作(或者,若Sharp為Java接口,則該方法可與任何實(shí)現(xiàn)了Sharp接口的類一起工作)
n 但是當(dāng)我們在實(shí)現(xiàn)子類時(shí)必須要謹(jǐn)慎對待,以確保我們不會(huì)無意中違反了LSP。

n 若一個(gè)函數(shù)未能滿足LSP,那么可能是因?yàn)樗@式地引用了超類的一些或所有子類。這樣的函數(shù)也違反了OCP,因?yàn)楫?dāng)我們創(chuàng)建一個(gè)新的子類時(shí),會(huì)不得不進(jìn)行代碼的修改。
LSP示例
n 考慮下面Rectangle類:


n 現(xiàn)在,Square類會(huì)如何呢?顯然,一個(gè)正方形是一個(gè)四邊形,因此Square類應(yīng)該從Rectangle類派生而來,對否?讓我們看一看!
n 觀察可得:
F 正方形不需要將高和寬都作為屬性,但是總之它將繼續(xù)自Rectangle。因此,每一個(gè)Square對象會(huì)浪費(fèi)一點(diǎn)內(nèi)存,但這并不是一個(gè)主要問題。
F 繼續(xù)而來的setWidth()和setHeight()方法對于Square而言并非真正地適合,因?yàn)橐粋€(gè)正方形的高和寬是相同。因此我們將需要重寫setWidth()和setHeight()方法。不得不重寫這些簡單的方法有可能是一種不恰當(dāng)?shù)睦^續(xù)使用方式。

n Square類如下:


n 看起來都還不錯(cuò)。但是讓我們檢驗(yàn)一下!




n 測試程序輸出:

n 看上去似乎我們違反了LSP!

n 這里的問題出在哪里呢?編寫testLsp()方法的程序員做了一個(gè)合理的假設(shè),即改變Rectangle的寬而保持它的高不變。
n 在將一個(gè)Square對象傳遞給這樣一個(gè)方法時(shí)產(chǎn)生了問題,顯然是違反了LSP
n Square和Rectangle類是相互一致和合法的。盡管程序員對基類作了合理的假設(shè),但其所編寫的方法仍然會(huì)導(dǎo)致設(shè)計(jì)模型的失敗。
n 不能孤立地去看待解決方案,必須根據(jù)設(shè)計(jì)用戶所做的合理假設(shè)來看待它們。

n 一個(gè)數(shù)學(xué)意義上的正方形可能是一個(gè)四邊形,但是一個(gè)Square對象不是一個(gè)Rectangle對象,因?yàn)橐粋€(gè)Square對象的行為與一個(gè)Rectangle對象的行為是不一致的!
n 從行為上來說,一個(gè)Square不是一個(gè)Rectangle!一個(gè)Square對象與一個(gè)Rectangle對象之間不具有多態(tài)的特征。
總結(jié)
n Liskov替換法則(LSP)清楚地表明了ISA關(guān)系全部都是與行為有關(guān)的。
n 為了保持LSP(并與開放-封閉法則一起),所有子類必須符合使用基類的client所期望的行為。
n 一個(gè)子類型不得具有比基類型(base type)更多的限制,可能這對于基類型來說是合法的,但是可能會(huì)因?yàn)檫`反子類型的其中一個(gè)額外限制,從而違反了LSP!
n LSP保證一個(gè)子類總是能夠被用在其基類可以出現(xiàn)的地方!

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 云和县| 安岳县| 奎屯市| 天峨县| 屏东市| 黑水县| 西乌珠穆沁旗| 新丰县| 乌兰浩特市| 瓮安县| 荆门市| 宝应县| 阳曲县| 阿鲁科尔沁旗| 边坝县| 绥化市| 如皋市| 鄯善县| 三台县| 新竹市| 革吉县| 同心县| 循化| 宁国市| 唐山市| 普定县| 高雄市| 玛曲县| 抚顺市| 富锦市| 富蕴县| 娱乐| 丹凤县| 阳江市| 鸡东县| 德钦县| 芒康县| 县级市| 宁都县| 临沧市| 乐都县|