面向?qū)ο笫且环N軟件開發(fā)的方法蓬痒,同類的還有面向過程泻骤。
面向?qū)ο笾傅氖窃诔绦蛟O(shè)計(jì)中采用Java的封裝梧奢、繼承狱掂、多態(tài)粹断、六大原則特性來設(shè)計(jì)代碼。
它其實(shí)考驗(yàn)的是你審視代碼的角度瓶埋,運(yùn)用這些特性,可以寫出讓人賞心悅目养筒、簡單易懂的代碼。
不運(yùn)用這些特性當(dāng)然也可以進(jìn)行開發(fā)晕粪。不過代碼的可讀性、擴(kuò)展性巫湘、靈活性等會(huì)大大下降,冗余度尚氛、維護(hù)成本等會(huì)大大上升。
封裝
- 概念
在Java中阅嘶,封裝就是將一些通用、常用的功能方法寫到一個(gè)新類中讯柔,那么當(dāng)我們用到這些功能時(shí),直接去調(diào)用這個(gè)新類中的方法即可魂迄。這就像是有一個(gè)人A,他擁有一些技能捣炬,當(dāng)我用到這些技能時(shí)怠晴,不需要自己去學(xué)習(xí)這些技能,只需要去找A即可。 - 優(yōu)點(diǎn)
提高代碼的重用稿械,減少重復(fù)代碼,提高代碼的可讀性美莫、方便理解。而且封裝的思想也對(duì)應(yīng)了Java中一處編程厢呵,到處執(zhí)行的思想。 - 實(shí)例
實(shí)例就不用多說了襟铭,平時(shí)寫代碼我們總會(huì)自己創(chuàng)建一個(gè)utils包碌奉,存放一些自己或者別人寫的utils類寒砖。
繼承
- 概念
繼承是從已有的類中派生出新的類,新的類能吸收已有類的數(shù)據(jù)屬性和行為哩都,并能擴(kuò)展新的能力(來自百度百科)。
這種官方語言太難講漠嵌,而且各位看官也看著費(fèi)勁。我還是直接說自己的理解吧儒鹿。
首先繼承的含義,就是一直我們口中所說的父類(基類)和子類挺身。子類通過關(guān)鍵字extends
繼承父類,就可以擁有父類的非私有的屬性和方法章钾。
在Java中,繼承是單一繼承+多層繼承的贱傀。 - 優(yōu)點(diǎn)
提高代碼的重用,減少重復(fù)的代碼府寒。增加了軟件開發(fā)的靈活性报腔。 - 缺點(diǎn)
由于多層繼承的存在,所以無限使用繼承特性的話纯蛾,會(huì)造成子類的過度冗余。
多態(tài)
- 概念
多態(tài)指的是同一個(gè)方法翻诉,會(huì)因?yàn)閷?duì)象的不同導(dǎo)致不同的結(jié)果。
沒錯(cuò)碰煌,就是這樣!
多態(tài)的三要素一定要知道芦圾。這個(gè)東西理解了自然就記住了俄认。
繼承个少、重寫梭依、父類的引用指向子類的對(duì)象。
具體含義役拴,還是在后面舉個(gè)栗子來解釋一下。 - 優(yōu)點(diǎn)
增加了軟件開發(fā)的靈活性河闰,簡化了編程和修改過程科平。 - 實(shí)例
首先我們定義了一個(gè)汽車接口Car
姜性,接口中有一個(gè)方法用來獲取車的類型:public interface Car { String getCarType(); }
接下來,我們創(chuàng)建了蘭博基尼部念,以及五菱宏光實(shí)現(xiàn)了這個(gè)接口。
五菱宏光:
public class WuLingHongGuang implements Car {
@Override
public String getCarType() {
return "五菱宏光";
}
}
蘭博基尼:
public class LanBoJiNi implements Car {
@Override
public String getCarType() {
return "蘭博基尼";
}
}
接下來儡炼,我們利用多態(tài)的特性,來創(chuàng)建并執(zhí)行接下來的代碼:
public static void main(String[] args){
Car car1 = new LanBoJiNi();
Car car2 = new WuLingHongGuang();
System.out.println("車1的類型:"+car1.getCarType());
System.out.println("車2的類型:"+car2.getCarType());
}
可以看到控制臺(tái)的結(jié)果:
車1的類型:蘭博基尼
車2的類型:五菱宏光
- 總結(jié)
通過實(shí)例乌询,再結(jié)合多態(tài)三要素:繼承、重寫妹田、父類的引用指向子類的對(duì)象鹃共。
蘭博基尼和五菱宏光實(shí)現(xiàn)了Car
接口(繼承)驶拱,重寫了Car
中的getCarType()
(重寫)霜浴,接下來最關(guān)鍵的要素蓝纲,我們用父類的引用,創(chuàng)建子類的對(duì)象驻龟。
那么接下來缸匪,當(dāng)你去調(diào)用getCarType()
時(shí)翁狐,Java會(huì)首先調(diào)用子類中的getCarType()
凌蔬,而不是父類Car
中的。
其實(shí)這個(gè)實(shí)例砂心,并不能幫你很好地理解多態(tài)。它只是很生硬地運(yùn)用了多態(tài)的特性辩诞。在項(xiàng)目中運(yùn)用多態(tài)非常的重要,這個(gè)需要自己實(shí)戰(zhàn)才能好好理解译暂。
六大原則
六大原則包括:單一職責(zé)、開閉外永、里氏替換、依賴倒置伯顶、接口隔離囚灼、迪米特祭衩。接下來我們一個(gè)一個(gè)來理解這些原則的思想。
單一職責(zé)原則(Single Responsibility Principle)
- 概念
就一個(gè)類而言汪厨,應(yīng)該僅有一個(gè)引起它變化的原因。
非常容易理解的一個(gè)原則劫乱,并且非常重要锥涕!但這個(gè)原則是一個(gè)備受爭議的原則,和別人爭論這個(gè)原則层坠,是屢試不爽的。
因?yàn)閱我宦氊?zé)原則劃分界限并不總是那么清晰破花,更多的時(shí)候是根據(jù)個(gè)人經(jīng)驗(yàn)來界定的。
開閉原則(Open Close Principle)
- 概念
就一個(gè)類或方法而言座每,應(yīng)該對(duì)于擴(kuò)展是開放的,對(duì)于修改是關(guān)閉的峭梳。
軟件也有自己的生命周期,越往后迭代葱椭,代碼越多,冗余度也會(huì)隨之提升孵运,維護(hù)成本也就越來越高,可能一次不經(jīng)意地bug修改就會(huì)破壞之前已經(jīng)通過測試的代碼治笨。因此,當(dāng)軟件需要變化時(shí)大磺,我們應(yīng)該通過擴(kuò)展的方式去實(shí)現(xiàn),而不是通過修改原有代碼來實(shí)現(xiàn)杠愧。
當(dāng)然,這是理想愿景流济,在實(shí)際開發(fā)中往往是擴(kuò)展和修改同時(shí)存在的,因?yàn)樵俸玫拇a绳瘟,終有一天也會(huì)有無法適應(yīng)的場景出現(xiàn)。所以糖声,我們要做的,就是在開發(fā)新東西的時(shí)候蘸泻,盡可能地考慮多的場景琉苇,盡可能降低修改的可能性悦施。并且當(dāng)我們發(fā)現(xiàn)代碼有“腐朽”的味道時(shí)并扇,應(yīng)該盡早地進(jìn)行代碼重構(gòu)抡诞,使代碼恢復(fù)到正常的“進(jìn)化”過程。
里氏替換原則(Liskov Substitution Principle)
- 概念
所有引用父類的地方昼汗,都可以透明的傳遞子類對(duì)象。
這個(gè)原則簡直就是多態(tài)的完美體現(xiàn)顷窒。
這個(gè)原則強(qiáng)調(diào)的是運(yùn)用多態(tài)時(shí),應(yīng)該注子類的適配蹋肮,使之無論傳遞任何子類對(duì)象璧疗,都能完美適應(yīng)父類引用坯辩,不會(huì)產(chǎn)生異常崩侠。 - 實(shí)例
我們繼續(xù)引用多態(tài)的那個(gè)實(shí)例,在那個(gè)實(shí)例之上做些修改却音。
現(xiàn)在我們是汽車制造商,你只需要告訴我品牌系瓢,我就可以生產(chǎn)出對(duì)應(yīng)品牌的車。
我們目前只能生產(chǎn)蘭博基尼
和五菱宏光
夷陋,那么接下來我們改變一下main方法,生產(chǎn)一下這2輛車:
public static void main(String[] args) {
//創(chuàng)建蘭博基尼
System.out.println("生產(chǎn)了一輛:" + createCar(new LanBoJiNi()));
//創(chuàng)建五菱宏光
System.out.println("生產(chǎn)了一輛:" + createCar(new WuLingHongGuang()));
}
private static String createCar(Car car) {
return car.getCarType();
}
上面當(dāng)中骗绕,我們通過createCar(Car car)
來創(chuàng)建了蘭博基尼
和五菱宏光
,雖然createCar()
要求的參數(shù)是Car
酬土,但是我們傳入了子類對(duì)象蘭博基尼
和五菱宏光
。并且這2個(gè)類都做了很好的適配(一個(gè)栗子而已,大家就認(rèn)為其實(shí)我的蘭博基尼其實(shí)是經(jīng)過500道獨(dú)特工序才制造出來的刹枉,五菱宏光是另外的500道工序),無論我傳入誰嘶卧,都可以完美生產(chǎn),不會(huì)產(chǎn)生異常芥吟。這就是里氏替換原則!
依賴倒置原則(Dependence Inversion Principle)
- 概念
依賴倒置原則指代了一種特定的解耦形式钟鸵,使得高層次的模塊不依賴低層次的模塊去實(shí)現(xiàn)細(xì)節(jié)。主要關(guān)鍵點(diǎn)有以下3點(diǎn):
高層模塊不應(yīng)該依賴低層模塊棺耍,兩者都應(yīng)該依賴其抽象。
抽象不應(yīng)該依賴細(xì)節(jié)蒙袍。
-
細(xì)節(jié)應(yīng)該依賴細(xì)節(jié)。
在Java語言中害幅,抽象指的就是接口或抽象類,兩者都是不能直接被實(shí)例化的以现;細(xì)節(jié)就是實(shí)現(xiàn)類,實(shí)現(xiàn)接口或者繼承抽象類而產(chǎn)生的類就是細(xì)節(jié)邑遏,可以直接被實(shí)例化。高層模塊就是抽象记盒,底層模塊就是具體實(shí)現(xiàn)類。一句話概括的話就是:面向接口編程纪吮。面向接口編程是面向?qū)ο蟮木柚弧?/p>
接口隔離原則(Interface Segregation Principles)
- 概念
類不應(yīng)該依賴它不需要的接口。
另一種定義是:類依賴的接口都應(yīng)該是最小單位彬碱。
那么接口隔離原則其實(shí)就是要求我們將龐大、臃腫的接口按照某種規(guī)則巷疼,將其拆封成更小的灵奖、更具體的接口,這樣客戶端只需要依賴它需要的接口即可瓷患。
接口隔離原則的目的就是使系統(tǒng)解耦,從而更容易進(jìn)行重構(gòu)擅编、更改等操作。
迪米特原則(Law of Demeter)
- 概念
一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有最少的依賴爱态。
通俗地講,一個(gè)類應(yīng)該對(duì)自己需要耦合或調(diào)用的類知道得最少锦担,類的內(nèi)部如何實(shí)現(xiàn)與調(diào)用者或者依賴者沒關(guān)系,調(diào)用者或依賴者只需要知道它需要的方法即可洞渔,其他的一概不用管。
如果兩個(gè)對(duì)象的關(guān)系過于密切磁椒,那么當(dāng)一個(gè)對(duì)象發(fā)生變化時(shí),另一個(gè)對(duì)象就會(huì)產(chǎn)生不可預(yù)估的風(fēng)險(xiǎn)衷快。
小結(jié)
在應(yīng)用的開發(fā)過程中,最難的不是完成應(yīng)用的開發(fā)工作,而是在后續(xù)的升級(jí)师郑、維護(hù)過程中讓應(yīng)用系統(tǒng)能夠擁抱變化。擁抱變化也就意味著在滿足需求且不破壞系統(tǒng)穩(wěn)定性的前提下保持高擴(kuò)展性宝冕、高內(nèi)聚、低耦合地梨,在經(jīng)歷了各版本的變更之后依然保持清晰、靈活宝剖、穩(wěn)定的系統(tǒng)架構(gòu)。
當(dāng)然這是一個(gè)理想的情況万细,但我們必須要朝著這個(gè)方向去努力,那么遵循面向?qū)ο笏枷刖褪俏覀冏呦蚶硐氲牡谝徊健?/p>
感謝
- 《Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》 何紅輝、關(guān)愛民 著
- 百度百科