Java多態的作用
🎉上一篇學習了多態的基礎語法,多態在實際開發中有什么作用呢?
我們先來了解一個業務背景:請設計一個系統,描述主人喂養寵物的場景,首先在這個場景當中應該有“寵物對象”,寵物對象應該有一個吃的行為,另外還需要一個“主人對象”,主人對象應該有一個喂的行為,請看代碼:
//寵物狗 public class Dog { String name; public Dog(String name){ this.name = name; } //吃的行為 public void eat(){ System.out.println(this.name + "在啃肉骨頭!"); } }
//主人 public class Master { //喂養行為 public void feed(Dog dog){ //主人喂養寵物,寵物就吃 System.out.println("主人開始喂食兒"); dog.eat(); System.out.println("主人喂食兒完畢"); } }
public class Test { public static void main(String[] args) { //創建狗對象 Dog dog = new Dog("二哈"); //創建主人對象 Master master = new Master(); //喂養 master.feed(dog); } }
運行結果如下圖所示:

以上程序編譯和運行都很正常,輸出結果也是對的,那么存在什么問題嗎?
假設后期用戶提出了新的需求,軟件可能面臨着功能擴展,這個擴展會很方便嗎?
假設現在主人家里又來了一個寵物貓,那該怎么辦呢?
在以上代碼的基礎之上,新增了一個Cat類,來表示寵物貓,這個對於程序來說是可以接受的:
//寵物貓 public class Cat { String name; public Cat(String name){ this.name = name; } //吃的行為 public void eat(){ System.out.println(this.name + "在吃魚!"); } }
另外,除了增加一個Cat類之外,我們還需要“修改”Master主人類的源代碼,這件事兒是我們程序員無法容忍的,因為修改之前寫好的源代碼就面臨着重新編譯、重新全方位的測試,這是一個巨大的工作,維護成本很高,也很麻煩:
//主人 public class Master { //喂養行為 public void feed(Dog dog){ //主人喂養寵物,寵物就吃 System.out.println("主人開始喂食兒"); dog.eat(); System.out.println("主人喂食兒完畢"); } //喂養行為 public void feed(Cat cat){ //主人喂養寵物,寵物就吃 System.out.println("主人開始喂食兒"); cat.eat(); System.out.println("主人喂食兒完畢"); } }
public class Test { public static void main(String[] args) { //創建狗對象 Dog dog = new Dog("二哈"); //創建主人對象 Master master = new Master(); //喂養 master.feed(dog); //創建貓對象 Cat cat = new Cat("湯姆"); //喂養 master.feed(cat); } }
運行結果如下圖所示:

在軟件開發過程中,有這樣的一個開發原則:開閉原則。開閉原則(OCP)是面向對象設計中“可復用設計”的基石,是面向對象設計中最重要的原則之一,其它很多的設計原則都是實現開閉原則的一種手段。
1988年,勃蘭特·梅耶(Bertrand Meyer)在他的著作《面向對象軟件構造(Object Oriented Software Construction)》中提出了開閉原則,它的原文是這樣:“Software entities should be open for extension,but closed for modification”。翻譯過來就是:“軟件實體應當對擴展開放,對修改關閉”。
這句話說得略微有點專業,我們把它講得更通俗一點,也就是:軟件系統中包含的各種組件,例如模塊(Modules)、類(Classes)以及功能(Functions)等等,應該在不修改現有代碼的基礎上,引入新功能。
開閉原則中“開”,是指對於組件功能的擴展是開放的,是允許對其進行功能擴展的;
開閉原則中“閉”,是指對於原有代碼的修改是封閉的,即修改原有的代碼對外部的使用是透明的。
以上程序在擴展的過程當中就違背了OCP原則,因為在擴展的過程當中修改了已經寫好的Master類,怎樣可以解決這個問題呢?多態可以解決,請看代碼:
//寵物類 public class Pet { String name; //吃的行為 public void eat(){ } }
//寵物貓 public class Cat extends Pet{ public Cat(String name){ this.name = name; } //吃的行為 public void eat(){ System.out.println(this.name + "在吃魚!"); } }
//寵物狗 public class Dog extends Pet{ public Dog(String name){ this.name = name; } //吃的行為 public void eat(){ System.out.println(this.name + "在啃肉骨頭!"); } }
//主人 public class Master { //喂養行為 public void feed(Pet pet){ //主人喂養寵物,寵物就吃 System.out.println("主人開始喂食兒"); pet.eat(); System.out.println("主人喂食兒完畢"); } }
public class Test { public static void main(String[] args) { //創建狗對象 Dog dog = new Dog("二哈"); //創建主人對象 Master master = new Master(); //喂養 master.feed(dog); //創建貓對象 Cat cat = new Cat("湯姆"); //喂養 master.feed(cat); } }
運行結果如下圖所示:

在以上程序中,Master類中的方法feed(Pet pet)的參數類型定義為更加抽象的Pet類型,而不是具體Dog寵物,或者Cat寵物,顯然Master類和具體的Dog、Cat類解耦合了,依賴性弱了,這就是我們通常所說的面向抽象編程,盡量不要面向具體編程,面向抽象編程會讓你的代碼耦合度降低,擴展能力增強,從而符合OCP的開發原則。假如說這會再來一個新的寵物豬呢,我們只需要這樣做,新增加一個“寵物豬類”,然后寵物豬類Pig繼承寵物類Pet,並重寫eat()方法,然后修改一下測試類就行了,整個過程我們是不需要修改Master類的,只是額外增加了一個新的類:
public class Pig extends Pet { public Pig(String name){ this.name = name; } //吃的行為 public void eat(){ System.out.println(this.name + "在吃粥!"); } }
public class Test { public static void main(String[] args) { //創建狗對象 Dog dog = new Dog("二哈"); //創建主人對象 Master master = new Master(); //喂養 master.feed(dog); //創建貓對象 Cat cat = new Cat("湯姆"); //喂養 master.feed(cat); //創建寵物豬對象 Pig pig = new Pig("小豬豬"); master.feed(pig); } }
運行結果如下圖所示:

以上程序中到底哪里使用了多態機制呢?請看下圖:

通過以上內容的學習,我們可以看到多態在開發中聯合方法覆蓋一起使用,可以降低程序的耦合度,提高程序的擴展力。在開發中盡可能面向抽象編程,不要面向具體編程,好比電腦主板和內存條的關系一樣,主板和內存條件之間有一個抽象的符合某個規范的插槽,不同品牌的內存條都可以插到主板上使用,2個G的內存條和4個G的內存條都可以插上,但最終的表現結果是不同的,2個G的內存條處理速度慢一些,4個G的快一些,這就是多態,所謂多態就是同一個行為作用到不同的對象上,最終的表現結果是不同的,主要的要求就是對象是可以進行靈活切換的,靈活切換的前提就是解耦合,解耦合依賴多態機制。
