摘要:概述和对比接口和类在实际使用中,一般都是配合使用的。接口是一个对外的协商约定。我们先实现一个交通工具的抽象类,然后继承实现轮船类。我们可以发现抽象类已经实现的方法,子类是无需重复实现的。
概述和对比
接口和类在实际使用中,一般都是配合使用的。我们来对比一下:
接口可以声明一个类的结构,包含属性和方法,但它只是给出一个声明,没有访问修饰符,没有具体的方法实现,没有构造函数,也不可以被实例化。
接口是一个对外的协商、约定。继承后才可以实例化,继承的时候必须实现声明。可以多重继承。
类是也是声明一个类的结构,包含属性和方法,有访问修饰符,有具体的方法实现,有构造函数,可以实例化(抽象类下面说)。继承的时候,可以覆盖,也可以继承使用父类实现。
抽象类也是类,不过它可以包含接口类似的特性:普通方法包含实现,抽象方法是不包含实现的;抽象类不能实例化。继承后才可以实例化,继承的时候必须实现抽象声明,其他声明和普通类一样。
补充说明一下,这里的接口、类与纯正的面向对象语言Java比,有一些不同的地方,有兴趣的可以自行了解。接口与类的使用场景
这里将给一个稍微复杂一点的例子:旅行社运送旅客。
定义接口这里我们定义了几类接口:旅客、载客、位置、运送,目的就是声明规范,实现了这些接口的类就可以被用来做这些事情。
// 旅客 interface Passgener { name: string; } // 载客 interface Carriable { passengers: Passgener[]; getUp (...passengers: Passgener[]): number; getOff (): number; } // 位置 interface Positioned { x: number; y: number; } // 常量:初始位置 const positionOrigin: Positioned = { x: 0, y: 0 }; // 运送 interface Moveable { position: Positioned; moveTo(Positioned: Positioned): Positioned; }实现具体类
这里我实现了两个类:小汽车、巴士,这两个类均实现了载客、运送接口,也就是说明它们是拥有这些能力的。
// 小汽车 class Car implements Carriable, Moveable { passengers: Passgener[]; position: Positioned; capacity: number; constructor(capacity: number) { this.passengers = []; this.position = positionOrigin; this.capacity = capacity; } getUp (...passengers: Passgener[]): number { if (passengers.length > this.capacity) { throw new Error(`This car can carry ${this.capacity} passengers!`); } console.log( `This car carries ${passengers.map(item => item.name).join(",")}` ); return this.passengers.push(...passengers); } getOff (): number { return this.passengers.splice(0).length; } moveTo(position: Positioned): Positioned { Object.assign(this.position, position); console.log( `This car carries ${this.passengers.map(item => item.name).join(",")} to [${ this.position.x }, ${this.position.y}]` ); return this.position; } } // 巴士 class Bus implements Carriable, Moveable { passengers: Passgener[]; position: Positioned; capacity: number; constructor(capacity: number) { this.passengers = []; this.position = positionOrigin; this.capacity = capacity; } getUp (...passengers: Passgener[]): number { if (passengers.length > this.capacity) { throw new Error(`This Bus can carry ${this.capacity} passengers!`); } console.log( `This Bus carries ${passengers.map(item => item.name).join(",")}` ); return this.passengers.push(...passengers); } getOff (): number { return this.passengers.splice(0).length; } moveTo(position: Positioned): Positioned { Object.assign(this.position, position); console.log( `This Bus carries ${this.passengers.map(item => item.name).join(",")} to [${ this.position.x }, ${this.position.y}]` ); return this.position; } }实现功能
具体类定义出来了,我们就可以实现功能,我们定义一个旅行社,由旅行社来运送旅客。
// 旅行社 class TravelAgency { name: string; constructor(name: string) { this.name = name; } carrying (carrier: Carriable & Moveable, position: Positioned, ...passengers: Passgener[]): Positioned { carrier.getUp(...passengers); return carrier.moveTo(position); } } // 实例化对象 let t1 = new TravelAgency("t1"); let c1 = new Car(4); let b1 = new Bus(30); // 实现功能 t1.carrying(c1, {x: 10, y: 60}, {name: "Jack"}, {name: "Tom"}); t1.carrying(b1, {x: 10, y: 60}, {name: "Jack"}, {name: "Tom"}, {name: "Mary"}, {name: "Joe"});抽象类的使用场景
上面已经把完整的功能实现出来了,但是我们可以看到,Car和Bus的代码还是有冗余的。这里我们就可以通过抽象类来做一些优化。
我们先实现一个交通工具的抽象类,然后继承实现轮船类。我们可以发现抽象类已经实现的方法,子类是无需重复实现的。只有抽象出来的必须子类实现的才需要实现。
// 交通工具 abstract class Vehicle implements Carriable, Moveable { name: string; passengers: Passgener[]; position: Positioned; capacity: number; // 抽象方法 abstract run(speed: number): void; getUp (...passengers: Passgener[]): number { if (passengers.length > this.capacity) { throw new Error(`This ${this.name} can carry ${this.capacity} passengers!`); } console.log( `This ${this.name} carries ${passengers.map(item => item.name).join(",")}` ); return this.passengers.push(...passengers); } getOff (): number { return this.passengers.splice(0).length; } moveTo(position: Positioned): Positioned { Object.assign(this.position, position); console.log( `This ${this.name} carries ${this.passengers.map(item => item.name).join(",")} to [${ this.position.x }, ${this.position.y}]` ); return this.position; } } // 轮船 class Ship extends Vehicle { constructor(capacity: number) { super(); this.passengers = []; this.position = positionOrigin; this.capacity = capacity; } // 抽象方法必须实现 run(speed: number) { // todo, run as a ship... console.log(`This ${this.name} running at ${speed}km/h.`); } }
到此介绍完了接口和类的基本使用场景,再次体会一下关键字:抽象、继承。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/110300.html
摘要:使用场景数据类型声明和约束声明数据类型使用数据类型面向对象编程这里和面向对象语言类似,用于定义对象接口,声明对象的结构,定义类时可以实现接口,满足这个接口定义的功能。 什么是接口 TypeScript的核心就是类型检查,接口就是用于声明类型,给内部或第三方使用者提供类型声明和约束。 使用场景 数据类型声明和约束 // 声明数据类型 interface CustomerInfo { ...
摘要:静态属性静态方法目前支持静态方法表示,类属性及静态属性目前作为提案还未正式成为标准。在中,抽象类不能用来实例化对象,主要做为其它派生类的基类使用。不同于接口,抽象类可以包含成员的实现细节。中也是这样规定的抽象类不允许直接被实例化。 尝试重写 在此之前,通过《JavaScript => TypeScript 入门》已经掌握了类型声明的写法。原以为凭着那一条无往不利的规则,就可以开开心心的...
摘要:值得注意的是,的返回值复写了原始的构造函数,原因是类装饰器必须返回一个构造器函数。原始构造函数的原型被复制给的原型,以确保在创建一个的新实例时,操作符如愿以偿,具体原因可参考鄙人另一篇文章原型与对象。 上一篇文章中,我们讨论了TypeScript源码中关于方法装饰器的实现,搞明白了如下几个问题: 装饰器函数是如何被调用的? 装饰器函数参数是如何传入的? __decorate函数干了...
摘要:前言是面对对象的语言,因此有必要单独纪录下对象的各种定义和理解。面对对象基本概述概述是基于面向过程的变成思想,是对面向过程的一种封装。面对对象开发就是不断的创建对象,使用对象,指挥对象做事情。面对对象设计其实就是在管理和维护对象之间的关系。 前言 java是面对对象的语言,因此有必要单独纪录下对象的各种定义和理解。 面对对象,主要包括:面向对象思想,类与对象及其使用,对象的内存图,成...
摘要:本文从装饰模式出发,聊聊中的装饰器和注解。该函数的函数名。不提供元数据的支持。中的元数据操作可以通过包来实现对于元数据的操作。 随着Typescript的普及,在KOA2和nestjs等nodejs框架中经常看到类似于java spring中注解的写法。本文从装饰模式出发,聊聊Typescipt中的装饰器和注解。 什么是装饰者模式 Typescript中的装饰器 Typescr...
阅读 2671·2021-11-18 10:02
阅读 3400·2021-09-28 09:35
阅读 2585·2021-09-22 15:12
阅读 740·2021-09-22 15:08
阅读 3070·2021-09-07 09:58
阅读 3463·2021-08-23 09:42
阅读 724·2019-08-30 12:53
阅读 2071·2019-08-29 13:51