多态实际含义:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈。
可能不太好理解,不要紧,看完下面两个例子,你就能明白了。
假如主人家里养了两只动物,分别是一只鸭和一只鸡,当主人向它们发出“叫”的命令时,鸭会“嘎嘎嘎”地叫,而鸡会“咯咯咯”地叫。这两只动物都会以自己的方式来发出叫声。它们同样“都是动物,并且可以发出叫声”,但根据主人的指令,它们会各自发出不同的叫声。
代码实现
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() );
这段代码虽然可以实现,但是还是存在一些问题,makeSound函数中只有鸡和鸭两种动物可以叫,如果来一只狗呢?显然,实现不了狗叫功能
解决办法1:添加一个分支语句
let makeSound = function(animal) {
if(animal instanceof Duck) {
console.log('嘎嘎嘎');
}else if(animal instanceof Chicken) {
console.log('咯咯咯');
}else(animal instanceof Dog){
console.log('汪汪汪');
}
}
显然这种改进方式是不合理的,世界上那么多动物都会叫,难道要写成千上万个if…else吗?
解决办法2:改进makeSound函数
let makeSound = function(animal) {
animal.sound();
}
let Duck = function() {};
Duck.prototype.sound = function() {
console.log('嘎嘎嘎');
}
let Chicken = function() {};
Chicken.prototype.sound = function() {
console.log('咯咯咯');
}
makeSound( new Duck() );
makeSound( new Chicken() );
如果有一天动物世界里添加了一条狗,那么我们只需要添加一个狗类即可,不需要往makeSound函数添加代码
let Dog = function() {};
Dog.prototype.sound = function() {
console.log('汪汪汪');
}
makeSound( new Dog() );
总结1
多态的思想实际上是把“做什么”和“谁去做”分离开来,要实现这一点,归根结底先要消除类型之间的耦合关系。某一种动物能否发出叫声,只取决于它有没有 makeSound 方法,而不取决于它是否是某种类型的对象,这里不存在任何程度上的“类型耦合”。这正是我们从上一节的鸭子类型中领悟的道理。
假如公司想要做一个地图应用,市面上有两家地图API供我们选择,谷歌和百度,我们选择了谷歌地图,一般会使用如下的编码方式
//谷歌使用show方法来渲染地图
let gooleMap = {
show: function() {
console.log('开始渲染谷歌地图');
}
}
//公司应用直接使用谷歌API
let renderMap = function() {
gooleMap.show();
}
renderMap(); //开始渲染谷歌地图
某一天,公司决定使用百度的渲染地图,需要重构原来的代码,这时候开发人员就会发现我们的renderMap函数不够健壮,没有一定的弹性,所以修改代码如下
//谷歌使用show方法来渲染地图
let gooleMap = {
show: function() {
console.log('开始渲染谷歌地图');
}
}
//百度使用show方法来渲染地图
let baiduMap = {
show: function() {
console.log('开始渲染百度地图');
}
}
//公司应用直接使用谷歌API
let renderMap = function(type) {
if(type === 'goole'){
gooleMap.show();
}else if(type === 'baidu'){
baiduMap.show();
}
}
renderMap('goole'); //开始渲染谷歌地图
renderMap('baidu'); //开始渲染百度地图
//这样就可以在百度和谷歌地图之间随时切换了
这样确实可以实现需求
但是,我们如果又想使用高德地图呢?又需要在renderMap中再添加一个分支语句?那这健壮性也还是不行啊,别急,代码还可以优化
let renderMap = function( map ){
if ( map.show instanceof Function ){
map.show();
}
};
//不管引进什么地图,我们的渲染函数都可以接受
let gaodeMap = {
show: function() {
console.log('开始渲染高德地图');
}
}
renderMap(gaodeMap);//渲染高德地图
renderMap(baiduMap);//渲染百度地图
renderMap(gooleMap);//渲染谷歌地图
总结2
将行为分布在各个对象中,这正是面向对象的优点。
Martin Fowler 在《重构:改善既有代码的设计》里写到:
多态的最根本好处在于,你不必再向对象询问“你是什么类型”而后根据得到的答案调用对象的某个行为——你只管调用该行为就是了,其他的一切多态机制都会为你安排妥当。
换句话说,多态最根本的作用就是通过把过程化的条件分支语句转化为对象的多态性,从而消除这些条件分支语句。
希望看完这篇文章的你在未来编程中可以少写一些if-else。