以上學(xué)習(xí)了多態(tài)的基礎(chǔ)語(yǔ)法,多態(tài)在實(shí)際開(kāi)發(fā)中有什么作用呢?我們先來(lái)了解一個(gè)業(yè)務(wù)背景:請(qǐng)?jiān)O(shè)計(jì)一個(gè)系統(tǒng),描述主人喂養(yǎng)寵物的場(chǎng)景,首先在這個(gè)場(chǎng)景當(dāng)中應(yīng)該有“寵物對(duì)象”,寵物對(duì)象應(yīng)該有一個(gè)吃的行為,另外還需要一個(gè)“主人對(duì)象”,主人對(duì)象應(yīng)該有一個(gè)喂的行為,請(qǐng)看代碼:
//寵物狗
public class Dog {
String name;
public Dog(String name){
this.name = name;
}
//吃的行為
public void eat(){
System.out.println(this.name + "在啃肉骨頭!");
}
}
//主人
public class Master {
//喂養(yǎng)行為
public void feed(Dog dog){
//主人喂養(yǎng)寵物,寵物就吃
System.out.println("主人開(kāi)始喂食兒");
dog.eat();
System.out.println("主人喂食兒完畢");
}
}
public class Test {
public static void main(String[] args) {
//創(chuàng)建狗對(duì)象
Dog dog = new Dog("二哈");
//創(chuàng)建主人對(duì)象
Master master = new Master();
//喂養(yǎng)
master.feed(dog);
}
}
運(yùn)行結(jié)果如下圖所示:
圖13-13:運(yùn)行結(jié)果
以上程序編譯和運(yùn)行都很正常,輸出結(jié)果也是對(duì)的,那么存在什么問(wèn)題嗎?假設(shè)后期用戶提出了新的需求,軟件可能面臨著功能擴(kuò)展,這個(gè)擴(kuò)展會(huì)很方便嗎?假設(shè)現(xiàn)在主人家里又來(lái)了一個(gè)寵物貓,那該怎么辦呢?請(qǐng)看代碼:
在以上代碼的基礎(chǔ)之上,新增了一個(gè)Cat類(lèi),來(lái)表示寵物貓,這個(gè)對(duì)于程序來(lái)說(shuō)是可以接受的:
//寵物貓
public class Cat {
String name;
public Cat(String name){
this.name = name;
}
//吃的行為
public void eat(){
System.out.println(this.name + "在吃魚(yú)!");
}
}
另外,除了增加一個(gè)Cat類(lèi)之外,我們還需要“修改”Master主人類(lèi)的源代碼,這件事兒是我們程序員無(wú)法容忍的,因?yàn)樾薷闹皩?xiě)好的源代碼就面臨著重新編譯、重新全方位的測(cè)試,這是一個(gè)巨大的工作,維護(hù)成本很高,也很麻煩:
//主人
public class Master {
//喂養(yǎng)行為
public void feed(Dog dog){
//主人喂養(yǎng)寵物,寵物就吃
System.out.println("主人開(kāi)始喂食兒");
dog.eat();
System.out.println("主人喂食兒完畢");
}
//喂養(yǎng)行為
public void feed(Cat cat){
//主人喂養(yǎng)寵物,寵物就吃
System.out.println("主人開(kāi)始喂食兒");
cat.eat();
System.out.println("主人喂食兒完畢");
}
}
public class Test {
public static void main(String[] args) {
//創(chuàng)建狗對(duì)象
Dog dog = new Dog("二哈");
//創(chuàng)建主人對(duì)象
Master master = new Master();
//喂養(yǎng)
master.feed(dog);
//創(chuàng)建貓對(duì)象
Cat cat = new Cat("湯姆");
//喂養(yǎng)
master.feed(cat);
}
}
運(yùn)行結(jié)果如下圖所示:
圖13-14:運(yùn)行結(jié)果
在軟件開(kāi)發(fā)過(guò)程中,有這樣的一個(gè)開(kāi)發(fā)原則:開(kāi)閉原則。開(kāi)閉原則(OCP)是面向?qū)ο笤O(shè)計(jì)中“可復(fù)用設(shè)計(jì)”的基石,是面向?qū)ο笤O(shè)計(jì)中最重要的原則之一,其它很多的設(shè)計(jì)原則都是實(shí)現(xiàn)開(kāi)閉原則的一種手段。1988年,勃蘭特·梅耶(Bertrand Meyer)在他的著作《面向?qū)ο筌浖?gòu)造(Object Oriented Software Construction)》中提出了開(kāi)閉原則,它的原文是這樣:“Software entities should be open for extension,but closed for modification”。翻譯過(guò)來(lái)就是:“軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉”。這句話說(shuō)得略微有點(diǎn)專(zhuān)業(yè),我們把它講得更通俗一點(diǎn),也就是:軟件系統(tǒng)中包含的各種組件,例如模塊(Modules)、類(lèi)(Classes)以及功能(Functions)等等,應(yīng)該在不修改現(xiàn)有代碼的基礎(chǔ)上,引入新功能。開(kāi)閉原則中“開(kāi)”,是指對(duì)于組件功能的擴(kuò)展是開(kāi)放的,是允許對(duì)其進(jìn)行功能擴(kuò)展的;開(kāi)閉原則中“閉”,是指對(duì)于原有代碼的修改是封閉的,即修改原有的代碼對(duì)外部的使用是透明的。
以上程序在擴(kuò)展的過(guò)程當(dāng)中就違背了OCP原則,因?yàn)樵跀U(kuò)展的過(guò)程當(dāng)中修改了已經(jīng)寫(xiě)好的Master類(lèi),怎樣可以解決這個(gè)問(wèn)題呢?多態(tài)可以解決,請(qǐng)看代碼:
//寵物類(lèi)
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 + "在吃魚(yú)!");
}
}
//寵物狗
public class Dog extends Pet{
public Dog(String name){
this.name = name;
}
//吃的行為
public void eat(){
System.out.println(this.name + "在啃肉骨頭!");
}
}
//主人
public class Master {
//喂養(yǎng)行為
public void feed(Pet pet){
//主人喂養(yǎng)寵物,寵物就吃
System.out.println("主人開(kāi)始喂食兒");
pet.eat();
System.out.println("主人喂食兒完畢");
}
}
public class Test {
public static void main(String[] args) {
//創(chuàng)建狗對(duì)象
Dog dog = new Dog("二哈");
//創(chuàng)建主人對(duì)象
Master master = new Master();
//喂養(yǎng)
master.feed(dog);
//創(chuàng)建貓對(duì)象
Cat cat = new Cat("湯姆");
//喂養(yǎng)
master.feed(cat);
}
}
運(yùn)行結(jié)果如下圖所示:
圖13-15:使用多態(tài)機(jī)制
在以上程序中,Master類(lèi)中的方法feed(Pet pet)的參數(shù)類(lèi)型定義為更加抽象的Pet類(lèi)型,而不是具體Dog寵物,或者Cat寵物,顯然Master類(lèi)和具體的Dog、Cat類(lèi)解耦合了,依賴(lài)性弱了,這就是我們通常所說(shuō)的面向抽象編程,盡量不要面向具體編程,面向抽象編程會(huì)讓你的代碼耦合度降低,擴(kuò)展能力增強(qiáng),從而符合OCP的開(kāi)發(fā)原則。假如說(shuō)這會(huì)再來(lái)一個(gè)新的寵物豬呢,我們只需要這樣做,新增加一個(gè)“寵物豬類(lèi)”,然后寵物豬類(lèi)Pig繼承寵物類(lèi)Pet,并重寫(xiě)eat()方法,然后修改一下測(cè)試類(lèi)就行了,整個(gè)過(guò)程我們是不需要修改Master類(lèi)的,只是額外增加了一個(gè)新的類(lèi):
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) {
//創(chuàng)建狗對(duì)象
Dog dog = new Dog("二哈");
//創(chuàng)建主人對(duì)象
Master master = new Master();
//喂養(yǎng)
master.feed(dog);
//創(chuàng)建貓對(duì)象
Cat cat = new Cat("湯姆");
//喂養(yǎng)
master.feed(cat);
//創(chuàng)建寵物豬對(duì)象
Pig pig = new Pig("小豬豬");
master.feed(pig);
}
}
運(yùn)行結(jié)果如下圖所示:
圖13-16:運(yùn)行結(jié)果
以上程序中到底哪里使用了多態(tài)機(jī)制呢?請(qǐng)看下圖:
圖13-17:哪里使用了多態(tài)機(jī)制
通過(guò)以上內(nèi)容的學(xué)習(xí),我們可以看到多態(tài)在開(kāi)發(fā)中聯(lián)合方法覆蓋一起使用,可以降低程序的耦合度,提高程序的擴(kuò)展力。在開(kāi)發(fā)中盡可能面向抽象編程,不要面向具體編程,好比電腦主板和內(nèi)存條的關(guān)系一樣,主板和內(nèi)存條件之間有一個(gè)抽象的符合某個(gè)規(guī)范的插槽,不同品牌的內(nèi)存條都可以插到主板上使用,2個(gè)G的內(nèi)存條和4個(gè)G的內(nèi)存條都可以插上,但最終的表現(xiàn)結(jié)果是不同的,2個(gè)G的內(nèi)存條處理速度慢一些,4個(gè)G的快一些,這就是多態(tài),所謂多態(tài)就是同一個(gè)行為作用到不同的對(duì)象上,最終的表現(xiàn)結(jié)果是不同的,主要的要求就是對(duì)象是可以進(jìn)行靈活切換的,靈活切換的前提就是解耦合,解耦合依賴(lài)多態(tài)機(jī)制。