后端框架中,几乎处处皆接口,可以说是面向接口编程。它的主要作用有二,一是用于约束其实现类应该具有哪些功能或行为;二是利用其语法特性-多态,实现依赖注入IOC。本节主要说作用一,作用二在《多态》章节中说。TS引入了接口(JS没有接口,TS编译为JS后,接口不会生成代码),它也可以用于约束其实现类的行为,但因为前端很少使用类,所以这个功能很少用到。在前端,接口主要用于定义字面量对象的类型,和type类似,在纯后端人眼里,可能会觉得即奇葩又鸡嫩。
一、C#中的接口
1.1 接口的定义和使用(作用)
//1、基本使用========================================================================
//接口的访问修饰符默认是public,接口成员不能加访问修饰符。最佳实践是接口不要加访问修饰符
//按照习惯,接口名称加前缀I,其它命名规则同“类”
interface IMovable
{
//接口成员可以是方法、属性和事件,绝大多数情况下都是用方法
//只有方法签名(方法名-参数-返回值),没有方法体(具体方法),具体方法在实现类中实现
void Move();
}
//Car称为实现类,在C#中继承和实现都是用“:”
//一个类只能继承一个类(详见《继承》章节),但可以实现多个接口
//如果实现多个接口,每个接口定义的方法,都要实现
public class Car : IMovable
{
//实现接口的方法Move,这里可以使用修改符了
public void Move()
{
Console.WriteLine("汽车在移动");
}
//实现类中可以有自己的字段、属性、方法、事件等成员
public string Name = "MC";
......
}
//2、实现多个接口===================================================================
interface IMovable
{
void Move();
}
interface IJumpable
{
void Jump();
}
public class Car : IMovable, IJumpable
{
public void Move(){...}
public void Jump(){...}
}
//3、接口可以继承接口================================================================
interface IBaseInterface
{
void Method1();
}
interface IDerivedInterface : IBaseInterface
{
void Method2();
}
class MyClass : IDerivedInterface
{
public void Method1(){...}
public void Method2(){...}
}
1.2 接口和抽象类的区别
- 抽象类的派生类必须实现所有抽象方法,接口的实现类中必须实现所有方法,两者行为一致
- 抽象类可以包含抽象成员和非抽象成员,抽象成员还可以有自己的实现和修饰符,只能单继承
- 接口只能包含方法、属性和事件的签名,不能有具体实现,没有修饰符,但可以多实现
- 抽像类还是类,C#中是单继承的,只能继承一个类,所以抽象类多用于规定派生类都应该做某件事
- 可以实现多个接口,接口之间也可以继承,所以接口多用于功能的组合(多态另说)
二、TS/UTS中的接口
2.1 用于约束其实现类的行为
//定义接口,接口成员可以是方法或属性
interface IPerson {
name: string;
printName(): void;
}
//接口之间可以继承
interface IPerson1 extends IPerson {}
//实现接口
class Person implements IPerson {
constructor(public name: string) {} //构造函数的属性简写方式,详见《类》章节
printName() {
console.log(this.name);
}
}
//多实现
interface IFirstInterface {
method1(): void;
}
interface ISecondInterface {
method2(): void;
}
class MyClass implements IFirstInterface, ISecondInterface {
method1(): void {...}
method2(): void {...}
}
2.2 用于定义字面量对象
interface Person {
name: string;
age?: number; // 可选属性方式一
age2: number | null; //可选属性方式二
readonly id: number; // 只读属性
sayHello(): void;
}
let person: Person = {
name: 'John',
id: 123,
sayHello() {
console.log('Hello!');
}
};
// 尝试修改只读属性会报错
// person.id = 456;
2.3 interface和type的区别
- 都可以,实现定义对象类型的基本功能,包括定义属性、方法、可选属性、只读属性等
- 接口,可以继承扩展,同名接口自动合并(详见《函数方法》中扩展方法的实现)
- type,通过交叉类型也可以实现类似继承,但不能有同名type
- type,除了定义对象类型外,可以定义任意类型的别名(联合和交叉)
- 实践中有人喜欢interface,有人喜欢type,咱们全栈的,建议type,让interface回归它原本的功能
- UTS注意事项:type的联合类型只有两种,纯字面量和可空组合,即type a = 1|2|3; type b = string | null