多態(tài)的含義
多態(tài)的實(shí)際含義是:同一操作作用于不同的對(duì)象什么全景,可以產(chǎn)生不同的解釋和不同的執(zhí)行結(jié)果耀石。換句話說(shuō),給不同的對(duì)象發(fā)送同一個(gè)消息的時(shí)候爸黄,這些對(duì)象會(huì)根據(jù)這個(gè)消息分別給出不同的反饋滞伟。
舉例說(shuō)明:
主人家里養(yǎng)了一只雞和一只鴨,當(dāng)主人向它們發(fā)出“叫”的指令時(shí)炕贵,鴨會(huì)“嘎嘎嘎”地叫梆奈,而雞會(huì)“咯咯咯”地叫。根據(jù)主人的指令称开,這兩只動(dòng)物都以各自的形式來(lái)響應(yīng)亩钟。
let makeSound = function(animal) {
if (animal instanceof Duck) {
console.log("嘎嘎嘎");
} else if (animal instanceof Chicken) {
console.log("咯咯咯");
}
}
let Duck = function() {};
let Chicken = function() {};
makeSound(new Duck()); // 嘎嘎嘎
makeSound(new Chicken()); // 咯咯咯
這段代碼體現(xiàn)了“多態(tài)”乓梨,但是這樣的“多態(tài)”并不能讓人滿意。如果后來(lái)又增加一條狗清酥,就必須得改動(dòng) makeSound 函數(shù)扶镀,才能讓狗發(fā)出叫聲。
多態(tài)背后的思想是將“做什么”和“誰(shuí)去做以及怎么做”分離開來(lái)焰轻,也就是將“不變的事物”與“可能改變的事物”分離開臭觉。在這里,動(dòng)物都會(huì)叫辱志,這是不變的蝠筑,但是不同類型的動(dòng)物具體怎么叫是可變的。把不變的部分隔離揩懒,把可變的部分封裝什乙,就可以賦予程序擴(kuò)展的能力,也是符合開放-封閉原則的已球,相對(duì)與修改代碼來(lái)說(shuō)稳强,僅增加代碼就能完成同樣的功能,這顯然優(yōu)雅和安全得多和悦。
對(duì)象的多態(tài)性
從靜態(tài)類型語(yǔ)言(如 Java)與動(dòng)態(tài)類型語(yǔ)言(如 Javascript)兩方面來(lái)考慮退疫。
動(dòng)態(tài)類型語(yǔ)言(Javascript)
首先把不變的部分隔離出來(lái),那就是所有的動(dòng)物都會(huì)發(fā)出叫聲鸽素。
let makeSound = function(animal) {
animal.sound();
}
然后把可變的部分各自封裝起來(lái)褒繁,也就是不同類型動(dòng)物的叫法。
let Duck = function() {};
Duck.prototype.sound = function() {
console.log("嘎嘎嘎");
};
let Chicken = function() {};
Chicken.prototype.sound = function() {
console.log("咯咯咯");
};
makeSound(new Duck()); // 嘎嘎嘎
makeSound(new Chicken()); // 咯咯咯
現(xiàn)在如果增加了一條狗馍忽,只要簡(jiǎn)單追加一些代碼就行了棒坏,不用改動(dòng)以前的 makeSound 函數(shù)。
靜態(tài)類型語(yǔ)言(Java)
靜態(tài)類型語(yǔ)言在編譯時(shí)會(huì)進(jìn)行類型匹配檢查遭笋。由于在代碼編譯時(shí)要進(jìn)行嚴(yán)格的類型檢查坝冕,所以不能給變量賦予不同類型的值。
要解決這一問(wèn)題瓦呼,歸根結(jié)底是先要消除類型之間的耦合關(guān)系喂窟。
如果類型之間的耦合關(guān)系沒(méi)有被消除,那么我們?cè)?makeSound 方法中指定了發(fā)出叫聲的對(duì)象是某個(gè)類型央串,它就不可能再被替換為另外一個(gè)類型磨澡,這一般是通過(guò)向上轉(zhuǎn)型來(lái)實(shí)現(xiàn)。
將 Duck 和 Chicken 不變的部分分隔開封裝到超類的抽象方法中质和,再分別讓兩者都繼承自抽象類 Animal稳摄。下述代碼中 (1) 和 (2) 處的賦值語(yǔ)句顯然是成立的,因?yàn)?Duck 和 Chicken 都是 Animal:
public abstract class Animal {
abstract void makeSound();
}
public class Chicken extends Animal {
public void makeSound() {
System.out.println("咯咯咯");
}
}
public class Duck extends Animal {
public void makeSound() {
System.out.println("嘎嘎嘎");
}
}
Animal duck = new Duck(); // (1)
Animal chicken = new Chicken(); // (2)
剩下的就是讓 AnimalSound 類的 makeSound 方法接受 Animal 類型的參數(shù)饲宿,而不是具體的 Duck 類型或者 Chicken 類型:
public class AnimalSound {
public void makeSound(Animal animal) { // 接受抽象類的參數(shù)
animal.makeSound();
}
}
public class Test {
public static void main(String args[]) {
AnimalSound animalSound = new AnimalSound();
Animal duck = new Duck();
Animal chicken = new Chicken();
animalSound.makeSound(duck); // 嘎嘎嘎
animalSound.makeSound(chicken); // 咯咯咯
}
}
兩者區(qū)別
多態(tài)思想實(shí)際上是把“做什么”和“誰(shuí)去做”分離開厦酬,要實(shí)現(xiàn)這一點(diǎn)胆描,歸根結(jié)底是先要消除類型之間的耦合關(guān)系。在靜態(tài)類型語(yǔ)言中仗阅,可以通過(guò)向上轉(zhuǎn)型來(lái)實(shí)現(xiàn)袄友。
而動(dòng)態(tài)類型語(yǔ)言的變量類型在運(yùn)行期是可變的。比如一個(gè) Javascript 對(duì)象霹菊,就既可以表示 Duck 類型的對(duì)象,又可以表示 Chicken 類型的對(duì)象支竹,這意味這動(dòng)態(tài)類型語(yǔ)言的多態(tài)是與生俱來(lái)的旋廷。這不難解釋,因?yàn)閯?dòng)態(tài)類型語(yǔ)言在編譯時(shí)沒(méi)有類型檢查的過(guò)程礼搁,所以不存在任何程度上的類型耦合饶碘,不需要向上轉(zhuǎn)型之類的技術(shù)來(lái)取得多態(tài)的效果。
設(shè)計(jì)模式與多態(tài)
在命令模式中馒吴,請(qǐng)求被封裝在一些命令對(duì)象中扎运,這使得命令的調(diào)用者和命令的接收者可以完全解耦開來(lái),當(dāng)調(diào)用命令的 execute 方法時(shí)饮戳,不同的命令會(huì)做出不同的事情豪治,從而會(huì)產(chǎn)生不同的執(zhí)行結(jié)果。而做這些事情的過(guò)程早已被封裝在命令對(duì)象內(nèi)部的扯罐,作為調(diào)用命令的客戶负拟,根本不必去關(guān)心命令執(zhí)行的具體過(guò)程。
在組合模式中歹河,多態(tài)性使得客戶可以完全忽略組合對(duì)象和葉節(jié)點(diǎn)對(duì)象之間的區(qū)別掩浙,這正是組合模式最大的作用所在。對(duì)組合對(duì)象和葉節(jié)點(diǎn)對(duì)象發(fā)出同一個(gè)消息的時(shí)候秸歧,它們會(huì)各自做自己應(yīng)該做的事情厨姚,組合對(duì)象把消息繼續(xù)轉(zhuǎn)發(fā)給下面的葉節(jié)點(diǎn)對(duì)象,葉節(jié)點(diǎn)對(duì)象則會(huì)對(duì)這些消息作出真實(shí)的反饋键菱。
在策略模式中谬墙,Context 并沒(méi)有執(zhí)行算法的能力,而是把這個(gè)職責(zé)委托給了某個(gè)策略對(duì)象经备。每個(gè)策略對(duì)象負(fù)責(zé)的算法已被各自封裝在對(duì)象內(nèi)部芭梯。當(dāng)我們對(duì)這些策略對(duì)象發(fā)出“計(jì)算”的消息時(shí),它們會(huì)返回各自不同的計(jì)算結(jié)果弄喘。
總結(jié)
Martin Fowler 在《重構(gòu):改善既有代碼的設(shè)計(jì)》里寫到:
多態(tài)最根本好處在于玖喘,你不必再向?qū)ο笤儐?wèn)“你是什么類型”而后根據(jù)得到的答案調(diào)用對(duì)象的某個(gè)行為——你只管調(diào)用該行為就是了,其它的一切多態(tài)機(jī)制都會(huì)為你安排妥當(dāng)蘑志。
換句話說(shuō)累奈,多態(tài)最根本的作用就是通過(guò)把過(guò)程化的分支語(yǔ)句轉(zhuǎn)化為對(duì)象的多態(tài)性贬派,從而消除這些條件分支語(yǔ)句。