Как стать автором
Обновить

Комментарии 24

class UserService

Хорошо — давать нормальные имена классам, которые будут давать примерное представление что в этих классах находится.
А «Service» это что?
Если класс называется UserService в него всё-равно в итоге напихают всего, что хоть как-то связано с пользователем.

Заголовок спойлера
Раз уж взялись за связность, привели бы примеры уменьшения от связности — геттеры, например, повыпиливать, которые вы советовали делать в предыдущей статье.

Документация по Angular, например, говорит что в сервисе происходит работа с данными. То есть созданный файл через angular-cli будет как раз называться user.service.ts, а класс в нем — UserService. Отсюда, мне кажется, что лучше не какие-то жесткие правила, а договоренности в определенной среде.


В Angular придет новый разработчик и будет знать, что в UserService происходит работа с данными юзера и при необходимости нужно смотреть туда. В других фронтенд-фреймворках, насколько я знаю, таких договоренностей нет, поэтому там может быть и UserManager, и UserController, и UserStore, да и вообще что угодно.

Договорённости конечно решают, и если такое соглашение на проекте есть то всё ок.
Но их частенько нет, а UserService, UserManager и прочие абсолютно неиформативные названия есть, обычно из-за человеческой лени и нежелания думать над неймингом, а это важно.
Иначе в один момент проект просто превращается в гору *Service и бесконтрольных связей.
Договоренности, договоренностями, но такие моменты еще можно и даже нужно отслеживать во время code revew.

Спасибо за статью! Хотел только отметить, что бОльшая часть правил из двух статей относится скорее не конкретно к TypeScript, а вообще к любому языку. Принципы разделения ответственности, иммутабельность и прочее — это просто Best Practices. Что касается именно TypeScript, то, на мой взгляд, следовало бы немного добавить про особенности фреймворков. Например, в Angular не рекомендуется ставить get на получение данных, используемых в шаблонах, так как это может привести к бесконечному Change Detection. В случае с Vue — get в TypeScript вполне успешно заменяет computed. И много других вариантов.


Разумеется, я понимаю, что на TypeScript пишут не только frontend и не только на фреймворках.

Да вы правы это в основном общие правила, которые можно применять к любому языку. Здесь в основном делается упор на то как эти правила можно применить именно в TS.
Мне ваше предложение по поводу рассмотреть какие-то особенности TypeScript и применяемость его в различных фреймворках, нравится. Подумаю о том чтобы выделить это отдельно.

Как и в первой есть спорные моменты.


Замечания к первой части:


  1. Enum должны быть строковыми, числовые, особенно с неявными значениями (без знака =) — абсолютное зло, т.к. дебажить их нереально.
  2. Гетеры и сетеры — зло (об этом уже писали). Не нужно повышать уровень магии в коде. А валидация должна быть внешней, либо в специальном методе.

Замечания ко второй части:


  1. Интерфейсы предпочтительнее типов, используйте типы только, если нельзя выразить интерфейсом. Пруф. type — это просто алис для типов (или комбинации типов), а interface полноценная сущность.
  2. Если и использовать fluent подход (цепочки), то обязательно иммутабельные (т.е. каждое звено должно генерировать клон). Но лучше обойтись, и писать явно.
    К примеру:
    const query = q('xxxx').where('y');
    const query2 = query.limit(2); // если цепочка будет мутабельной, то мы неявно поменяем и query, что часто хотелось бы избежать

Используйте иммутабельность

Как связаны иммутабельность и readonly интерфейсы? Можно преспокойно заимплементить readonly интерфейс, состояние имплементации которого будет мутировать на ура.
Классы должны быть маленькими
Высокая сплоченность низкая связь
Предпочитайте композицию наследованию
Используйте цепочки вызовов

А причем тут Typescript?
Это общие моменты подхода чистый код, и примеры как его применять в TypeScript

Хотел бы добавить про иммутабельность. Вообще, readonly действительно работает только с примитивами, то есть, когда речь идет о присваивании. Но, как уже было указано выше, существуют дополнительные типы вроде Readonly<YourInterface> или ReadonlyArray<SomeOtherInterface>, которые будут кидать ошибку при попытке изменить значение любым способом.


Единственный минус данного подхода, да и в целом TypeScript — это отсутствие интерфейсов в рантайме, что, в теории, дает возможность мутировать значение. Но и для этого есть свой метод — старый добрый Object.freeze. Правда, если возникает такая необходимость, то появляются вопросы к организации процесса разработки и тестирования.

Readonly интерфейсы можно реализовать геттерами и вот тут уже как раз ни о какой иммутабельности речи не идет. Объект может поменять самостоятельно свое внутреннее состояние, а ссылка на объект будет та же.
Я это к тому, что если где-то в коде в Typescript вы получили readonly объект, не нужно ожидать, что он будет иммутабельным. Более того, это опасно ожидать, что он будет иммутабельным.

Может, есть какой-то более конкретный пример? Потому что пока это все выглядит как способ выстрелить себе в ногу. Да, если в классе стоит readonly someClass = new SomeClass() — это не означает, что он будет иммутабельный сам по себе. Это означает, что ему нельзя присвоить новое значение.


Если же это будет простой объект с данными или массив, то есть сущности, которые ничего не делают сами по себе, то тогда readonly или Readonly<ISomeInterface> — это еще какой выход.

Может, есть какой-то более конкретный пример?

Какой-нибудь прокси класс или фасад, который может просто перепрокидывать данные из другого класса через readonly property, причем он может даже создавать их на лету при каждом вызове.
Надеятся на то, что в каждый момент времени через то же самое проперти одного и того же инстанса объекта будут приходить одни и те же данные не стоит. И вот это уже НЕ иммутабельность. Вопрос не в том, что readonly interface это плохо, я этого не говорил. Я лишь к тому, что readonly interface != immutability.

Readonly это круто, используйте это. Просто если вам нужна иммутабельность как она есть, то может стоит посмотреть в сторону каких-то решений, которые имеют отношение к иммутабельности. К примеру, github.com/immutable-js/immutable-js

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

Immutable data cannot be changed once created

Согласен, спасибо.

как-то все вообщем, напихали название SOLID спагетти с typescript.

А еще вопрос, какой смысл от такого массива?

const array: ReadonlyArray = [ 1, 3, 5 ];

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

Высокая. Всё-таки TypeScript является статически типизированным, и конструкция array.push(5) в нём просто не скомпилируется.

Property 'push' does not exist on type 'readonly number[]' не скомпилится
Ну и есть высокая вероятность, что не будут, смысл такого массива не ясен
я имею ввиду следующее, часто Вам попадался на практике такой код и какого его применение?

Что именно попадалось?
ReadonlyArray<number>? Да, часто.
Литерал массива? Да, тоже часто.
Вот вместе, и в форме локальной переменной они мне не попадались.

Вот вместе, и в форме локальной переменной они мне не попадались. — я об этом

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории