面向對象是一種軟件開發的方法,同類的還有面向過程。
面向對象指的是在程序設計中采用Java的封裝、繼承、多態、六大原則特性來設計代碼。
它其實考驗的是你審視代碼的角度,運用這些特性,可以寫出讓人賞心悅目、簡單易懂的代碼。
不運用這些特性當然也可以進行開發。不過代碼的可讀性、擴展性、靈活性等會大大下降,冗余度、維護成本等會大大上升。
封裝
- 概念
在Java中,封裝就是將一些通用、常用的功能方法寫到一個新類中,那么當我們用到這些功能時,直接去調用這個新類中的方法即可。這就像是有一個人A,他擁有一些技能,當我用到這些技能時,不需要自己去學習這些技能,只需要去找A即可。 - 優點
提高代碼的重用,減少重復代碼,提高代碼的可讀性、方便理解。而且封裝的思想也對應了Java中一處編程,到處執行的思想。 - 實例
實例就不用多說了,平時寫代碼我們總會自己創建一個utils包,存放一些自己或者別人寫的utils類。
繼承
- 概念
繼承是從已有的類中派生出新的類,新的類能吸收已有類的數據屬性和行為,并能擴展新的能力(來自百度百科)。
這種官方語言太難講,而且各位看官也看著費勁。我還是直接說自己的理解吧。
首先繼承的含義,就是一直我們口中所說的父類(基類)和子類。子類通過關鍵字extends
繼承父類,就可以擁有父類的非私有的屬性和方法。
在Java中,繼承是單一繼承+多層繼承的。 - 優點
提高代碼的重用,減少重復的代碼。增加了軟件開發的靈活性。 - 缺點
由于多層繼承的存在,所以無限使用繼承特性的話,會造成子類的過度冗余。
多態
- 概念
多態指的是同一個方法,會因為對象的不同導致不同的結果。
沒錯,就是這樣!
多態的三要素一定要知道。這個東西理解了自然就記住了。
繼承、重寫、父類的引用指向子類的對象。
具體含義,還是在后面舉個栗子來解釋一下。 - 優點
增加了軟件開發的靈活性,簡化了編程和修改過程。 - 實例
首先我們定義了一個汽車接口Car
,接口中有一個方法用來獲取車的類型:public interface Car { String getCarType(); }
接下來,我們創建了蘭博基尼,以及五菱宏光實現了這個接口。
五菱宏光:
public class WuLingHongGuang implements Car {
@Override
public String getCarType() {
return "五菱宏光";
}
}
蘭博基尼:
public class LanBoJiNi implements Car {
@Override
public String getCarType() {
return "蘭博基尼";
}
}
接下來,我們利用多態的特性,來創建并執行接下來的代碼:
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());
}
可以看到控制臺的結果:
車1的類型:蘭博基尼
車2的類型:五菱宏光
- 總結
通過實例,再結合多態三要素:繼承、重寫、父類的引用指向子類的對象。
蘭博基尼和五菱宏光實現了Car
接口(繼承),重寫了Car
中的getCarType()
(重寫),接下來最關鍵的要素,我們用父類的引用,創建子類的對象。
那么接下來,當你去調用getCarType()
時,Java會首先調用子類中的getCarType()
,而不是父類Car
中的。
其實這個實例,并不能幫你很好地理解多態。它只是很生硬地運用了多態的特性。在項目中運用多態非常的重要,這個需要自己實戰才能好好理解。
六大原則
六大原則包括:單一職責、開閉、里氏替換、依賴倒置、接口隔離、迪米特。接下來我們一個一個來理解這些原則的思想。
單一職責原則(Single Responsibility Principle)
- 概念
就一個類而言,應該僅有一個引起它變化的原因。
非常容易理解的一個原則,并且非常重要!但這個原則是一個備受爭議的原則,和別人爭論這個原則,是屢試不爽的。
因為單一職責原則劃分界限并不總是那么清晰,更多的時候是根據個人經驗來界定的。
開閉原則(Open Close Principle)
- 概念
就一個類或方法而言,應該對于擴展是開放的,對于修改是關閉的。
軟件也有自己的生命周期,越往后迭代,代碼越多,冗余度也會隨之提升,維護成本也就越來越高,可能一次不經意地bug修改就會破壞之前已經通過測試的代碼。因此,當軟件需要變化時,我們應該通過擴展的方式去實現,而不是通過修改原有代碼來實現。
當然,這是理想愿景,在實際開發中往往是擴展和修改同時存在的,因為再好的代碼,終有一天也會有無法適應的場景出現。所以,我們要做的,就是在開發新東西的時候,盡可能地考慮多的場景,盡可能降低修改的可能性。并且當我們發現代碼有“腐朽”的味道時,應該盡早地進行代碼重構,使代碼恢復到正常的“進化”過程。
里氏替換原則(Liskov Substitution Principle)
- 概念
所有引用父類的地方,都可以透明的傳遞子類對象。
這個原則簡直就是多態的完美體現。
這個原則強調的是運用多態時,應該注子類的適配,使之無論傳遞任何子類對象,都能完美適應父類引用,不會產生異常。 - 實例
我們繼續引用多態的那個實例,在那個實例之上做些修改。
現在我們是汽車制造商,你只需要告訴我品牌,我就可以生產出對應品牌的車。
我們目前只能生產蘭博基尼
和五菱宏光
,那么接下來我們改變一下main方法,生產一下這2輛車:
public static void main(String[] args) {
//創建蘭博基尼
System.out.println("生產了一輛:" + createCar(new LanBoJiNi()));
//創建五菱宏光
System.out.println("生產了一輛:" + createCar(new WuLingHongGuang()));
}
private static String createCar(Car car) {
return car.getCarType();
}
上面當中,我們通過createCar(Car car)
來創建了蘭博基尼
和五菱宏光
,雖然createCar()
要求的參數是Car
,但是我們傳入了子類對象蘭博基尼
和五菱宏光
。并且這2個類都做了很好的適配(一個栗子而已,大家就認為其實我的蘭博基尼其實是經過500道獨特工序才制造出來的,五菱宏光是另外的500道工序),無論我傳入誰,都可以完美生產,不會產生異常。這就是里氏替換原則!
依賴倒置原則(Dependence Inversion Principle)
- 概念
依賴倒置原則指代了一種特定的解耦形式,使得高層次的模塊不依賴低層次的模塊去實現細節。主要關鍵點有以下3點:
高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象。
抽象不應該依賴細節。
-
細節應該依賴細節。
在Java語言中,抽象指的就是接口或抽象類,兩者都是不能直接被實例化的;細節就是實現類,實現接口或者繼承抽象類而產生的類就是細節,可以直接被實例化。高層模塊就是抽象,底層模塊就是具體實現類。一句話概括的話就是:面向接口編程。面向接口編程是面向對象的精髓之一。
接口隔離原則(Interface Segregation Principles)
- 概念
類不應該依賴它不需要的接口。
另一種定義是:類依賴的接口都應該是最小單位。
那么接口隔離原則其實就是要求我們將龐大、臃腫的接口按照某種規則,將其拆封成更小的、更具體的接口,這樣客戶端只需要依賴它需要的接口即可。
接口隔離原則的目的就是使系統解耦,從而更容易進行重構、更改等操作。
迪米特原則(Law of Demeter)
- 概念
一個對象應該對其他對象有最少的依賴。
通俗地講,一個類應該對自己需要耦合或調用的類知道得最少,類的內部如何實現與調用者或者依賴者沒關系,調用者或依賴者只需要知道它需要的方法即可,其他的一概不用管。
如果兩個對象的關系過于密切,那么當一個對象發生變化時,另一個對象就會產生不可預估的風險。
小結
在應用的開發過程中,最難的不是完成應用的開發工作,而是在后續的升級、維護過程中讓應用系統能夠擁抱變化。擁抱變化也就意味著在滿足需求且不破壞系統穩定性的前提下保持高擴展性、高內聚、低耦合,在經歷了各版本的變更之后依然保持清晰、靈活、穩定的系統架構。
當然這是一個理想的情況,但我們必須要朝著這個方向去努力,那么遵循面向對象思想就是我們走向理想的第一步。
感謝
- 《Android源碼設計模式解析與實戰》 何紅輝、關愛民 著
- 百度百科