2016-10-14 4 views
1

Я использую внешнюю выборку API, а затем отображаю данные.Абстрактные внешние API

Чтобы быть гибким, я хочу полностью отделить API от моего кода и захотеть работать с самонастраиваемыми структурами данных.

Вот краткий графический обзор: enter image description here

Чтобы сделать задачу немного больше здесь для бетона пример:

Давайте предположим, что данные о людях:

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 { 

} 

И это где я застрял! Я не знаю, как объединить их вместе!

мне не нравится идея экземпляра на экземпляр лица:

let aPerson = new Person(new Parser(data)) 

Поскольку анализатор должен быть без гражданства (например, как функция).

Проблема заключается в машинописном не позволяет мне сделать это с классами:

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) 
    } 
} 

Callbacks является вариантом, но только если я могу подтвердить свою подпись.

Например, это не проверяет правильность:

type PersonDataParser = (data : string) => IPersonDataStructure; 

// Whoops.. argument is missing! 
let aParser =() => { 
    return {...} 
} 

let aPerson = new Person('data', aParser) 

Любые предложения о том, как решить эту проблему?

+0

В чем именно проблема, которую вы пытаетесь решить здесь? – toskv

+0

Я хочу реализовать описанную функциональность чистым способом. – d3L

+0

Это не вопрос типа handle stackoverflow. Возможно, попробуйте веб-сайт обмена обновлениями кода. Здесь он будет генерировать множество решений, основанных на мнениях. :) – toskv

ответ

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

Почему бы не создать адаптер, который будет проверять, какие из двух свойств были возвращены из 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 
     ); 
    } 
} 
Смежные вопросы