простые объекты экземпляры классов VS для объектов модели

Как лучше всего создавать объекты модели в Angular / TypeScript:

  1. Следует ли использовать аннотацию типа с обозначением объекта (объекты - это простые экземпляры Object)? Например. let m: MyModel = { name: 'foo' }

  2. Следует ли использовать оператор new (объекты являются экземплярами соответствующего прототипа)?

  3. Следует ли смешивать эти два подхода и использовать их ситуативно? (Например, простые объекты при получении ответа от HttpClient, но new MyModel('foobar') для удобства создания экземпляров путем передачи свойств в качестве аргументов конструктора)

Предыстория этого вопроса

Я новичок в TypeScript и, как и многие другие разработчики, пришел из популярных объектно-ориентированных языков, таких как Java.

Я понял ключевую концепцию: аннотации типов в TypeScript не играют роли во время выполнения. Они важны для компилятора и для автоматического завершения вашего редактора. В основном это ECMAScript с добавлением проверок типов во время компиляции.

В то время я не знал этого и ожидал, что TypeScript будет чем-то вроде "Java на стороне клиента", я нашел этот отчет об ошибке Angular: https://github.com/angular/angular/issues/20770

Люди (которые не понимают концепцию типов TypeScript) жалуются на HttpClient не преобразование возвращенного простого объекта в тип их класса модели. Другие люди защищают это поведение и указывают на то, что в JavaScript нет типов времени выполнения.

И здесь возникает моя проблема: на самом деле в JavaScript ЕСТЬ типы времени выполнения. Вы можете создавать экземпляры прототипов с помощью оператора new и даже проверять их конструктор с помощью оператора instanceof.

В TypeScript у вас есть два способа создания экземпляра:

1) Используйте обозначение объекта (как показано в руководстве по Angular ):

hero: Hero = {
  id: 1,
  name: 'Windstorm'
};

2) Используйте new-оператор:

hero: Hero = new Hero();

На данный момент я работаю над проектом, в котором смешаны эти два варианта. Т.е. объекты модели одного и того же типа являются экземплярами Object в некоторых случаях и экземплярами Hero в других случаях.

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

Моя идея правила / лучшей практики заключалась в том, чтобы определить все экземпляры модели как простые объекты и использовать конструктор для служб, компонентов и т. Д., Которые создаются путем внедрения зависимостей. В результате я бы вообще не стал использовать оператор new.

Однако я не уверен, что это хорошая идея, и не смог найти передовой практики рекомендации по этому поводу.

ИЗМЕНИТЬ

Важное примечание для близких избирателей: я не ищу здесь вашего личного мнения. Я скорее ищу какую-то официально задокументированную передовую практику Angular, поскольку я считаю, что это основное дизайнерское решение, которое должно быть принято с самого начала проекта, и эти подходы не должны смешиваться случайным образом без определенной причины. Возможно, ответ прост: «Нет официальной рекомендации, какое решение принимать».


person fishbone    schedule 08.08.2018    source источник
comment
Мое личное мнение по этому поводу: если вы используете функции из своих объектов, создайте экземпляр класса. Если вы работаете только со свойствами, введите свой ответ в виде интерфейса.   -  person    schedule 08.08.2018
comment
Я также вижу проблему с объектами модели, содержащими массивы вложенных объектов. С помощью конструктора вы можете предоставить пустой массив. Свойство простого объекта будет undefined, и вероятность исключения во время выполнения выше.   -  person fishbone    schedule 08.08.2018
comment
Это не проблема простых объектов, это проблема вашего собственного кода. Это все равно, что сказать, что существует проблема с классами, потому что он не создает пустые массивы, если вы не записываете его в конструктор.   -  person    schedule 08.08.2018
comment
Мой комментарий был дополнением к твоему. Свойства массива - еще один случай, когда истинные экземпляры имеют преимущество перед простыми объектами.   -  person fishbone    schedule 08.08.2018
comment
Я знаю, я исправляю то, что вы сказали, потому что считаю это неправдой. И снова экземпляры классов не имеют преимущества перед простыми объектами: оба обрабатывают массивы одинаково.   -  person    schedule 08.08.2018


Ответы (2)


На мой взгляд, вы должны использовать оператор new для создания новых объектов вашего класса, если вам нужно выполнить сложные операции над самим объектом.

Если в случае, если вам просто нужно получить доступ только к свойствам, вы можете использовать литерал объекта для создания нового объекта.

У этого есть такие преимущества, как инкапсуляция, наследование и т. Д., К которым разработчик, имеющий опыт работы на Java, мог бы лучше относиться.

Если вы напрямую назначаете объект, как показано ниже, вам нужно явно указать в нем функции, например, getName function.

hero: Hero = {
  id: 1,
  name: 'Windstorm',
  getName: function(){ // returns name }
};

Однако, определив класс Hero с помощью функции getName и затем создав экземпляр этого класса, вы автоматически получите этот function независимо от того, сколько раз вы создаете экземпляры.

Чтобы лучше понять объектно-ориентированный Javascript, вы можете перейти по ссылке ниже: -

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object-oriated_JS


Что касается проблемы ниже,

«Люди (которые не понимают концепцию типов TypeScript) жалуются, что HttpClient не преобразует возвращаемый простой объект в тип их класса модели».

HttpClient просто возвращает объект, который отправляет сервер, он находится на JS, чтобы преобразовать его в требуемую форму. Вы всегда можете сопоставить ответ с требуемым экземпляром модели, используя его конструктор, который можно определить следующим образом: -

constructor(hero){
  this.id = hero.id;
  this.name = hero.name;
}

и вы можете сопоставить массив, возвращаемый с сервера, как показано ниже: -

map(hero => new Hero(hero))
person Ankit Sharma    schedule 08.08.2018
comment
Я ценю ваше мнение, однако я ищу либо официальную передовую практику, либо хочу знать, есть ли типичный способ решения этой проблемы, потому что со временем это оказалось лучшей практикой, и почти все делают это таким образом. Мол, goto is evil - это устоявшаяся рекомендация. Я не хотел начинать здесь обсуждение, так как SO - неподходящее место для этого. Если нет установленной передовой практики, я начну обсуждение в форуме / группе новостей. - person fishbone; 08.08.2018
comment
Официального документа, в котором излагаются лучшие методы создания объектов, не существует. Однако это полностью зависит от разработчика и его опыта. Использование classes и оператора new просто делает приложение более понятным, например, для Java-разработчика. Фактически, используя оператор new, вы получаете широкий спектр возможностей, которые вы можете использовать, возможно, при расширении своего приложения, когда вам, возможно, придется писать функции для этого объекта / модели. - person Ankit Sharma; 08.08.2018
comment
Вы можете просмотреть следующие сообщения sitepoint.com/ и stackoverflow.com/questions/6843951/ - person Ankit Sharma; 08.08.2018

На самом деле в JavaScript ЕСТЬ типы времени выполнения.

Вид. Значение может иметь определенный тип, однако переменные и свойства не имеют привязанных к ним типов. Тем не менее, если вы вводите сетевые запросы и т. Д.

  fetch("someurl").then(res => res.json()) as Promise<IUser>

и этот сетевой запрос внезапно возвращает что-то еще, кроме пользователя, ничего не произойдет, поскольку типы Typescript не существуют во время выполнения. Однако это поведение можно легко добавить с помощью typeguards:

  function isUser(user: any) : user is IUser {
     return Number.isInteger(user.id) && typeof user.name === "string";
  }

что позволяет выполнять проверку типов во время выполнения:

 const result = await fetch("someurl");
 if(!isUser(result)) throw new Error();
 // result is definetly a IUser here

Стоит мне использовать наследование или нет?

Это вопрос предпочтений. Однажды я написал приложение, которое интенсивно работает с базой данных, поэтому мне пришлось выполнить много сериализации / десериализации, и меня очень раздражало то, что всегда переносить ответ из базы данных в экземпляр класса. Итак, для своих моделей БД я начал использовать следующий шаблон:

  type User = {
   name: string;
   id: number;
 };

 const User = {
   get(id: number): User { /*...*/ }
   changeName(user: User, name: string) { /*..*/ }
};

Это позволило мне написать:

 const admin: User = User.get(123);

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

Будет ли это хорошим шаблоном для вашего варианта использования или нет, на самом деле зависит от того, сколько сериализации / десериализации вы выполняете.

person Jonas Wilms    schedule 08.08.2018