概念:
泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。
泛型应用:
场景:在做前后端交互数据接口统一返回格式定义的场景来进行泛型的实战训练。
- 通过ID查询指定数据返回的报文格式:
- 通过查询整个列表返回的报文格式:
通过分析两份返回报文可以得到一些信息:
- 公共且确定类属性
code
和message
; - 公共但类型不确定的属性result,可能是对象也可能是数组;
- 返回列表数据的报文时list对象中需要包含公共的属性size和pages;
- 返回列表数据中的单个元素和返回指定数据的对象都包含一个公共的id字段,可以抽取到公共父类中使用,因为数据库必须有数据唯一的标识;
通过UML类图来看一下没有泛型的结构
BaseResponse:
抽取接口公共属性:code,message,result
class BaseResponse { code: number; message: string; result: any; constructor(code = 200, message: string, result: any) { this.code = code; this.message = message; this.result = result; } }
BaseData:
模拟数据库表数据的唯一主键id
class BaseData { id: number; constructor(id: number) { this.id = id; } }
Person:
模拟数据库人员表单条数据
class Person extends BaseData { name: string; age: number; constructor(id: number, name: string, age: number) { super(id); this.name = name; this.age = age; } }
ListPersonData:
模拟数据库人员表多条数据
class ListPersonData { size: number; pages: number; persons: Person[]; constructor(size: number, pages: number, persons: Person[]) { this.size = size; this.pages = pages; this.persons = persons; } }
ListPersonResponse:
class ListPresonResponse extends BaseResponse { // 组装数据 static toResponse(): object { const person = new Person(100, "zhangsan", 18); const result = new ListPersonData(20, 1, [person]); const listDataResponse = new ListPresonResponse( 200, "数据加载完成", result ); return listDataResponse; } }
PersonResponse:
class PersonResponse extends BaseResponse { // 组装数据 static toResponse(): object { const person = new Person(100, "zhangsan", 18); const personResponse = new PersonResponse(200, "数据加载完成", person); return personResponse; } }
无泛型结构说明:
通过输出我们可以看到需求已经实现了,也可以看得出来类直接的关系比较紧密,不利于扩展和维护。我们看一看应用泛型后有什么变化吧。
对类进行升级-应用泛型:
1. 对BaseResponse升级支持泛型:
class BaseResponse<T> { code: number; message: string; result: T; constructor(code = 200, message: string, result: T) { this.code = code; this.message = message; this.result = result; } }
2. 对报错的ListPresonResponse配置泛型类型:
class ListPresonResponse extends BaseResponse<ListPersonData> { // 组装数据 static toResponse(): object { const person = new Person(100, "zhangsan", 18); const result = new ListPersonData(20, 1, [person]); const listDataResponse = new ListPresonResponse( 200, "数据加载完成", result ); return listDataResponse; } }
- 配置泛型类型前的类型提示:(因为类型不确定只能是any)
- 配置泛型类型后的类型提示:
3. 对报错的PersonResponse配置泛型类型:
class PersonResponse extends BaseResponse<Person> { // 组装数据 static toResponse(): object { const person = new Person(100, "zhangsan", 18); const personResponse = new PersonResponse(200, "数据加载完成", person); return personResponse; } }
- 配置泛型类型前的类型类型提示:(因为类型不确定只能是any)
- 配置泛型类型后的类型类型提示:
4. 返回数据列表的ListPersonData类我们应用泛型后进行改造,抽取为统一的ListData类
切记ListData为公共类不显示的关联其他类,原来的persons属性改为list属性
class ListData<T extends BaseData> { size: number; pages: number; list: T[]; constructor(size: number, pages: number, list: T[]) { this.size = size; this.pages = pages; this.list = list; } }
5. 调整ListPresonResponse符合我们的泛型规定
class ListPresonResponse extends BaseResponse<ListData<Person>> { // 组装数据 static toResponse(): object { const person = new Person(100, "zhangsan", 18); const result = new ListData(20, 1, [person]); const listDataResponse = new ListPresonResponse( 200, "数据加载完成", result ); return listDataResponse; } }
应用泛型后说明:
应用泛型后的代码变得更容易扩展,我们如果新增一个商品表的话,对应的单条数据返回和多条数据返回的结构将可以很方便的在原来的基类上进行扩展了,如果需要在基类中进行属性的扩展和变更将受影响到所有子类中。 我们来看一下为商品对象报文增加的类:
class Goods extends BaseData { // 实现内部属性 } class ListGoodsResponse extends BaseResponse<ListData<Goods>> { // 实现组装数据函数(模拟使用) } class GoodsResponse extends BaseResponse<Goods> { // 实现组装数据函数(模拟使用) }
结语:
泛型的应用使我们的程序在设计过程中对结构进行抽象但不指明具体类的类型,使的基类变得有规矩的宽松,延时到进行子类的实现时再补充泛型类型进行明确。