我正在使用外部API提取,然后显示数据。
为了灵活,我想完全将API与我的代码分离,并希望使用自定义的数据结构。
为了使这个任务更加具体,这里有一个例子:
我们假设数据是关于人的:
API v1.0吐出{"name": "John"}
,而API v1.1吐出{"pName": "John"}
。
为了防止这种微小的改变会破坏我的代码,我想在内部有两个接口:一个用于解析器(解析API响应),另一个用作数据本身的结构:
interface IPersonDataStructure {
name : string;
}
interface IPersonDataParser {
parse(input: string) : IPersonDataStructure;
}
然后我想要一个将解析器与数据结构相结合的类:
// This class uses any parser which implements IPersonDataParser
// And uses IPersonDataStructure
class Person {
}
这就是我被困住的地方!我不知道如何将两者结合在一起!
我不喜欢每个Person实例的实例的想法:
let aPerson = new Person(new Parser(data))
因为解析器应该是无状态的(例如像函数一样)。
问题是TypeScript不允许我使用类来执行此操作:
class Parser implements IPersonDataParser {
static public function parse(data : string) : IPersonDataStructure {
return {...}
}
}
class Person {
private _data : IPersonDataStructure;
constructor(data : string, parser : IPersonDataParser) {
this._data = parser.parse(data)
}
}
回调是一种选择,但前提是我可以验证他们的签名。
例如,这不能正确验证:
type PersonDataParser = (data : string) => IPersonDataStructure;
// Whoops.. argument is missing!
let aParser = () => {
return {...}
}
let aPerson = new Person('data', aParser)
有关如何解决此问题的任何建议?
答案 0 :(得分:1)
首先,您可以使用静态方法并使其满足接口 - 所有这些都使用类型推断和结构类型,如下所示:
interface IPersonDataStructure {
name : string;
}
interface IPersonDataParser {
parse(input: string) : IPersonDataStructure;
}
class Parser {
public static parse(data : string) : IPersonDataStructure {
return { name: 'Steve' };
}
}
class Person {
private _data : IPersonDataStructure;
constructor(data : string, parser : IPersonDataParser) {
this._data = parser.parse(data)
}
}
let person = new Person('', Parser);
我可能更喜欢一种设计,其中Person
只代表一个人,并且没有必要接受映射器才能构建。更像这样......
interface IPersonDataStructure {
name : string;
}
class Person {
constructor(private data : IPersonDataStructure) {
}
}
class PersonMapper {
public static map(data: string): Person {
return new Person({
name: 'Steve'
});
}
}
let person = PersonMapper.map('...');
如果您的版本号是数据的一部分,您可以使用它来确定正确的映射。
答案 1 :(得分:1)
为什么不创建一个适配器来检查从API返回的两个属性中的哪一个?
interface ApiResponse {
name?: string;
pName?: string;
}
class Person {
public name: string;
constructor (name: string) {
this.name = name;
}
}
class ApiResponseAdapter {
private getName(response: ApiResponse): string {
if (response.pName) return pName;
if (response.name) return name;
// if neither are set, return null
return null;
}
public adapt(response: ApiResponse): Person {
let name = this.getName(response);
if (name === null) {
throw new Error("Invalid name for response: " + JSON.stringify(response));
}
return new Person(name);
}
}
或者你可以有一个基础ApiResponse
接口,它具有处理行为的实现:
interface ApiResponse {
name: string;
}
class Api_V1_0_Response implements ApiResponse {
public name: string;
constructor (json: any) {
this.name = json["name"];
}
}
class Api_V1_1_Response implements ApiResponse {
public name: string;
constructor (json: any) {
this.name = json["pName"];
}
}
class Person {
public name: string;
constructor (name: string) {
this.name = name;
}
}
class ApiResponseAdapter {
public adapt(response: ApiResponse): Person {
return new Person(
response.name
);
}
}
或者更进一步,有一个抽象的BaseApiResponse
类,由另外两个扩展:
interface ApiResponse {
name: string;
}
abstract class BaseApiResponse implements ApiResponse {
public name: string;
constructor (nameKey: string, json: any) {
this.name = json[nameKey];
}
}
class Api_V1_0_Response extends BaseApiResponse {
constructor (json: any) {
super("name", json);
}
}
class Api_V1_1_Response extends BaseApiResponse {
constructor (json: any) {
super("pName", json);
}
}
class Person {
public name: string;
constructor (name: string) {
this.name = name;
}
}
class ApiResponseAdapter {
public adapt(response: ApiResponse): Person {
return new Person(
response.name
);
}
}