设计模式——多态
【前言】
当我们谈论JavaScript设计模式时,多态是一个非常重要的概念。在面向对象编程中,多态是指同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈。
【正文】
举例说明
主人家里养了两只动物,分别是一只鸭和一只鸡,当主人向它们发出“叫”的命令时,鸭会“嘎嘎嘎”地叫,而鸡会“咯咯咯”地叫。这两只动物都会以自己的方式来发出叫声。它们同样“都是动物,并且可以发出叫声”,但根据主人的指令(同一操作),它们会各自发出不同的叫声(不同的反馈)。
下面我们用代码来描述上述需求。
const makeSound = function( animal ){
if ( animal instanceof Duck ){
console.log( ’嘎嘎嘎’ );
}else if ( animal instanceof Chicken ){
console.log( ’咯咯咯’ );
}
}
const Duck = function(){};
const Chicken = function(){};
makeSound( new Duck() ); // 嘎嘎嘎
makeSound( new Chicken() ); // 咯咯咯
这段代码确实体现了“多态性”,当我们分别向鸭和鸡发出“叫唤”的消息时,它们根据此消息作出了各自不同的反应。
但这样的“多态性”是无法令人满意的,如果后来又增加了一只动物,比如狗,显然狗的叫声是“汪汪汪”如果这时候主人家又来一条狗,我们需要怎么改写上述代码呢?
const makeSound = function( animal ){
if ( animal instanceof Duck ){
console.log( ’嘎嘎嘎’ );
}else if ( animal instanceof Chicken ){
console.log( ’咯咯咯’ );
}
//增加的代码
else if ( animal instanceof Dog ){
console.log( ’汪汪汪’ );
}
}
const Duck = function(){};
const Chicken = function(){};
const Dog = function(){};
makeSound( new Duck() ); // 嘎嘎嘎
makeSound( new Chicken() ); // 咯咯咯
makeSound( new Dog() ); // 汪汪汪
这时候我们需要去修改makeSound函数的代码,而且增加一次判断。
修改代码总是危险的,修改的地方越多,程序出错的可能性就越大,而且当动物的种类越来越多时,makeSound有可能变成一个巨大的函数。
下面我们将改写这个危险的代码
多态背后的思想是将“做什么”和“谁去做以及怎样去做”分离开来,也就是将“不变的事物”与“可能改变的事物”分离开来。
在这个故事中,动物都会叫,这是不变的,但是不同类型的动物具体怎么叫是可变的。把不变的部分隔离出来,把可变的部分封装起来。
const makeSound = function( animal ){
//不可变
animal.sound();
};
const Duck = function(){}
Duck.prototype.sound = function(){
//可变
console.log( ’嘎嘎嘎’ );
};
const Chicken = function(){}
Chicken.prototype.sound = function(){
//可变
console.log( ’咯咯咯’ );
};
makeSound( new Duck() ); //嘎嘎嘎
makeSound( new Chicken() ); //咯咯咯
//增加狗
const Dog=function(){}
Dog.prototype.sound=function(){
//可变
console.log(’汪汪汪’);
};
makeSound(newDog()); //汪汪汪
ts版本
interface Animal {
sound: () => void;
}
const makeSound = (animal: Animal): void => {
animal.sound();
};
class Duck implements Animal {
sound(): void {
console.log('嘎嘎嘎');
}
}
class Chicken implements Animal {
sound(): void {
console.log('咯咯咯');
}
}
makeSound(new Duck()); //嘎嘎嘎
makeSound(new Chicken()); //咯咯咯
//增加狗
class Dog implements Animal {
sound(): void {
console.log('汪汪汪');
}
}
我不关心你到底是什么动物,只要你有sound方法就行。
修改后的代码是我们的程序更利于扩展,程序看起来是可生长的,也是符合开放—封闭原则的,相对于修改代码来说,仅仅增加代码就能完成同样的功能,这显然优雅和安全得多。
其实在我们平时开发过程中上述的需求其实很多,以下是一段实战的代码。
实战代码
interface Map {
show: () => void;
}
class GoogleMap implements Map {
show(): void {
console.log('开始渲染谷歌地图');
}
}
class BaiduMap implements Map {
show(): void {
console.log('开始渲染百度地图');
}
}
// 不利于扩展
// const renderMap = (type: 'goole'|'baidu'):void => {
// if(type === 'goole){
// GoogleMap.show()
// }else if( type === "baidu"){
// BaiduMap.show()
// }
// }
// renderMap('goole'); // 输出:开始渲染谷歌地图
// renderMap('baidu'); // 输出:开始渲染百度地图
//利于扩展
const renderMap = (map: Map): void => {
if (map.show instanceof Function) {
map.show();
}
};
const googleMap = new GoogleMap();
const baiduMap = new BaiduMap();
renderMap(googleMap); // 输出:开始渲染谷歌地图
renderMap(baiduMap); // 输出:开始渲染百度地图
总结
- 在我们日常开发中,我们要多思考变与不变,抽象出不变的部分。
- 有一个名词很有意思,叫鸭子类型。解释就是,如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子。多态思想其实和这个类型很像,我不管你是什么动物,只要你会叫,我不管你是什么地图,只要你有渲染方法。
参考资料
[1] JavaScript设计模式和开发实践
转载自:https://juejin.cn/post/7222179242955702327