JavaScript 接口和面向接口编程
什么是接口
接口是对象能响应的请求的集合
我们经常说一个库或者模块对外提供了某某 API 接口。通过主动暴露的接口来通信,可以隐 藏软件系统内部的工作细节。这也是我们最熟悉的第一种接口含义。
第二种接口是一些语言提供的关键字,比如 Java 的 interface。interface 关键字可以产生一 个完全抽象的类。这个完全抽象的类用来表示一种契约,专门负责建立类与类之间的联系。
第三种接口即是我们谈论的“面向接口编程”中的接口,接口的含义在这里体现得更为抽象。
Java中的抽象类
java中为了实现对象的多态性会使用向上转型的超类型来作为值的约束,这个向上转型的超类型就是抽象类或者interface。
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( "嘎嘎嘎" );
}
}
public class AnimalSound{
public void makeSound( Animal animal ){ // 接收 Animal 类型的参数,而非 Duck 类型或 Chicken 类型
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 ); // 输出:咯咯咯
}
}
抽象类除了向上转型外,它还有有契约约束的作用,子类必须实现接口约束的makeSound
方法。
由于存在约束,在编译器中就可以在编写代码阶段就提供错误提示来提醒程序员。
总而言之,不关注对象的具体类型,而仅仅针对超类型中的“契约方法”来编写程序,可以 产生可靠性高的程序,也可以极大地减少子系统实现之间的相互依赖关系,这就是我们本章要讨 论的主题:
面向接口编程,而不是面向实现编程。
那抽象类和接口又有什么关系呢?实际上这里的接口并不是单指具体的某个事物,而是一个抽象的概念,使用抽象类编程,其实可以看成是“面向超类型编程”,具体的对象被隐藏在超类型背后。
而“面向接口编程”也可以看成是面向抽象编程,即针对超类型中的 abstract 方法编程,接口 在这里被当成 abstract 方法中约定的契约行为。这些契约行为暴露了一个类或者对象能够做什 么,但是不关心具体如何去做。
interface
除了用抽象类来完成面向接口编程,我们还可以使用interface来达到同样的效果。
interface更像是一个完全抽象的类,因为抽象类本身还可以携带具体的方法,而interface不提供任何具体的实现,但是允许interface确定方法名、参数列表、返回类型。
interface 同样可以用于向上转型,这也是让对象表现出多态性的一条途径,实现了同一个接 口的两个类就可以被相互替换使用。
public interface Animal{
abstract void makeSound();
}
public class Duck implements Animal{
public void makeSound() { // 重写 Animal 接口的 makeSound 抽象方法
System.out.println( "嘎嘎嘎" );
}
}
public class Chicken implements Animal{
public void makeSound() { // 重写 Animal 接口的 makeSound 抽象方法
System.out.println( "咯咯咯" );
}
}
public class AnimalSound {
public void makeSound( Animal animal ){
animal.makeSound();
}
}
public class Test {
public static void main( String args[] ){
Animal duck = new Duck();
Animal chicken = new Chicken();
AnimalSound animalSound = new AnimalSound();
animalSound.makeSound( duck ); // 输出:嘎嘎嘎
animalSound.makeSound( chicken ); // 输出:咯咯咯
}
}
JavaScript是否需要抽象类和interface
通过前面的讲解,我们明白了抽象类和 interface 的作用主要都是以下两点。
- 通过向上转型来隐藏对象的真正类型,以表现对象的多态性。
- 约定类与类之间的一些契约行为。
对于JavaScript而言,向上转型是一种天生就有的能力,我们不需要关注对象真正的类型,而是只要这个对象有我要的属性或者方法(鸭子类型),它就是正确的类型。
由于不需要向上转型,抽象类和interface最大的作用就是检查代码的规范性,也这是我们后来使用TS的一个最大需求。
用鸭子类型来进行接口检查
由于JavaScript中并不存在抽象类和接口,但是为了程序运行的正确性,我们常常会编写一些鸭子类型思想的接口检查:
var isArray = function(obj) {
return obj &&
typeof obj === 'object' &&
typeof obj.length === 'number' &&
typeof obj.splice === 'function'
};
后来我们渐渐的改用通过判断原型上的默认类名来实现更准确的判断:Object.prototype.toString.call( [] ) === '[object Array]'
。
但是我们不总是进行接口检查,如果所有的接口都要进行检查是不明智的,也是不必要的,毕竟到目前为止还没有一种通用且好用的方式来进行接口检查,并且这种检查与具体的业务逻辑无关。
TypeScript中的interface
这个其实没啥好说的,在ts中我们可以编写interface实现接口约束,这样在编写代码阶段就能通过编辑器判断是否有无漏写。
但是即便是vscode,在有些时候也不是那么准确,经常需要重启vscode来重新载入ts检查,期望以后能做的更好吧。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据