Узнайте, как максимизировать возможности повторного использования в ваших угловых компонентах, используя композицию вместо наследования
Это продолжение моей предыдущей статьи о компоненте компонента с Angular, где я перечислил 3 способа сочинения угловых компонентов:
Класс наследство
Классовые микшины
Компонент композиции
TLDR; Мой любимый способ – сочинять компоненты в небольших единицах и использовать входы и выходы для связи между компонентами. Чтобы поделиться логическими частями между компонентами, мне нравится, как микшины могут помочь нам избежать некоторых ловушек от использования наследования класса.
В этой статье я хочу больше сосредоточиться на взаимосвязи между наследством класса и классовыми микшинами, как они отличаются, и некоторые ловушки от использования микшинов для создания компонентов.
Совет: Используйте такие инструменты, как ** bit ** ( github ), чтобы увеличить повторное использование кода путем обмена и сотрудничества на угловых компонентах по проектам Анкет Поделитесь своими многоразовыми строительными блоками в коллекции на bit.dev , для будущих композиций.
Пример: Общие угловые компоненты в немного коллекции
Подводные камни наследства классов
Вы, вероятно, уже знаете, почему использование наследования иногда очень привлекательно: один раз определяйте некоторые методы и свойства, а затем используйте их для каждого общего подкласса: Замечательно!
На поверхностном уровне, а в некоторых случаях это на самом деле хорошо. Тем не менее, есть некоторые известные и задокументированные проблемы, которые связаны с *наследством *класса. *Наиболее важные, с точки зрения компонента архитектора, являются следующими:
хрупкий базовый класс – когда изменение в вашем базовом классе разбивает полученные подклассы
Это поощряет ранний выбор при разработке базового класса: он делает дизайн хрупким и хрупким
это нарушает инкапсуляцию
На самом деле, вы, возможно, слышали легендарную цитату из Банда из четырех книга:
Есть несколько типов компонентов, где я вижу наследство, используемое довольно часто:
Поля формирования с аксессуами общего значения
Компоненты маршрута, которые расширяют базовую маршруту
Модальные, всплывающие окна и т. Д. С общими методами (покажите, скрыть и т. Д.)
Эта статья больше фокусируется на бизнес -логике, а не на чисто визуальных атрибутах (отключенных, анимированных и т. Д.). Я обнаружил, что разделение логики между компонентами является немного сложной и в основном неправильно понятой темой, особенно когда сама структура не обеспечивает официальную позицию по поводу этой темы, как напротив, например, для реагирования.
TypeScript Mixins
Концепция микшинов довольно проста: представьте, что вместо иерархии классов у вас вместо этого есть несколько очень маленьких частичных классов. Эти классы могут быть объединены вместе и создавать большие классы с большой гибкостью.
То, как микшины создаются с помощью TypeScript, прост: мы определяем функцию, которая принимает в качестве аргумента класс и расширяем недавно созданный класс с помощью одного, пройденного в качестве аргумента.
Во -первых, мы определяем микшины пинмиксин и Closemixin, которые определяют 1 метод каждый:
function pinMixin(BaseClass) {
return class extends BaseClass {
pin() {
// implementation
}
}
}
function closeMixin(BaseClass) {
return class extends BaseClass {
close() {
// implementation
}
}
}
Мы создаем базовый класс, который создается путем слияния функций Mixins, а затем расширяем реализацию:
const BaseTabMixin = pinMixin(
closeMixin(class {})
);
class Tab extends BaseTabMixin {}
// Tab now can use the methods `close` and `pin`
Сценарий: приложение агрегатора социальных сетей
Например, я хочу построить прототип приложения агрегатора в социальных сетях с помощью публикаций сообщений из основных социальных сетей.
Это особый пример, с которым я столкнулся много лет назад в качестве младшего разработчика: Babel был выпущен, и классы ES6 были новой модной вещью, пока они не стали.
Junior Me, немного наивно, начал создавать базовые классы, простирание влево и вправо, и это было захватывающе. Посмотрите, сколько кода я смог поделиться благодаря им! В начале это то, что вы не сразу понимаете: требования не полностью обозначены, и, как мы все знаем, новые детали возникают непрерывно.
Мы увидим, как создавать компоненты сообщений для социальных сетей, таких как Facebook, Twitter, YouTube и Reddit: Во -первых, мы будем использовать старое старое наследство.
После этого мы будем рефакторировать с помощью композиции.
Создание базового пост -компонента с наследством
Давайте продолжим и создадим класс Basepost, который разделяет свойства и методы, которыми поделится полученные подклассы. Как вы, возможно, уже знаете, сообщения в социальных сетях довольно похожи друг на друга, с тонкими различиями: у них есть автор, какой -то контент (будь то текст, ссылка или изображение), и разрешают некоторые действия, такие как симпатия, обмен, редактирование и т. д.
Наш пост -компонент базового класса будет иметь вход (объект POST) и будет вводить службу Postervice, в которую мы делегируем наши действия.
Единственное распространенное действие, разделенное среди всех социальных постов, удаляется, и поэтому мы добавляем его в базовый класс, чтобы все подклассы могли наследовать метод.
class PostComponent {
@Input() post: Post;
constructor(protected service: PostService) {}
delete() {
this.post.delete(this.post.id);
}
}
Это минимальный базовый класс, который мы можем создать. Теперь мы можем продолжить и добавить конкретные действия.
Мы знаем, что и Facebook, и Twitter позволяют любить сообщения, но не в Twitter или YouTube; Поэтому мы создаем подкласс под названием «LikeablePost:
class LikeablePost extends PostComponent {
get likes() {
return this.post.likes;
}
like() {
this.service.like(this.post.id);
}
unlike() {
this.service.unlike(this.post.id);
}
}
И YouTube, и Reddit позволяют посадкам быть пропагандированы и понижены; Имеет смысл создать подкласс, который позволяет выполнять такие действия:
class VoteablePost extends PostComponent {
downvote() {
this.service.downvote(this.post.id);
}
upvote() {
this.service.upvote(this.post.id);
}
}
Facebook и Twitter также имеют еще одно сходство: концепция «обмена» в качестве ключевых метаданных.
class ShareablePost extends LikeablePost {
get shares() {
return this.post.shares;
}
share() {
this.service.share(this.post.id);
}
}
Сходство, разделенное среди YouTube, Facebook и Reddit, заключается в том, что все они позволяют отредактировать сообщения, в отличие от Twitter.
Это первая проблема, с которой мы сталкиваемся:
Поскольку метод не разделен всем классам, было бы ошибкой добавить его в базовый класс
Мы могли бы реализовать метод редактирования для всех подклассов, но это было бы очень повторяющимся
Мы продолжаем внедрять TwitterPostComponent
@Component({...})
class TwitterPostComponent extends ShareablePost {}
Давайте прыгнем в будущее, и Джек даст нам ужасные новости: мы больше не можем удалять твиты! Наш класс теперь должен измениться, но подождите: удаление определено в базовом классе.
Если мы удалим метод из базового класса, мы сломаем другие классы
Если мы удалим его только из TwitterBaseComponent, мы в конечном итоге нарушим принцип замены Лискова , это означает, что TwitterBaseComponent и PostComponent должен быть в состоянии поменять, не нарушая ничего
Если это было достаточно ясно, все это было плохой идеей.
Введите композицию
Теперь мы собираемся переписать все предыдущие, вместо этого составив мини-классы и используя TypeScript Mixins для создания компонентов из многих отдельных, небольших классов.
Давайте создадим микшины, необходимые для создания компонента TwitterPostComponent: LikeMixin, Deletemixin и Sharemixin.
Базовый класс
Прежде всего, мы хотим, чтобы микшины были достаточно общими, чтобы применяться к различным компонентам, причем одной отдельной зависимостью является услуга, введенная в компонент.
export interface PostComponent {
post: Post;
service: PostService;
}
Likeemixin
// like
function likeMixin>(
Base: IBasePost
) {
return class extends BasePost implements CanLike {
get likes() {
return this.post.likes;
}
like() {
return this.service.like(this.post.id);
}
unlike() {
return this.service.unlike(this.post.id);
}
};
}
Deletemixin
function deleteMixin>( BasePost: IBasePost ) { return class extends BasePost implements CanDelete { delete() { return this.service.delete(this.post.id); } }; }
Шаримиксин
*export function shareMixin>( BasePost: IBasePost ) { return class extends BasePost implements CanShare { shares: number; share() { return this.service.share(this.post.id); } }; }
Создание компонента реализации: TwitterPostComponent
После создания мы можем применить их к недавно созданному TwitterPostComponent:
const TwitterBase = deleteMixin(
likeMixin(
shareMixin(PostComponent)
)
);
Если вы предпочитаете использовать функцию ApplyMixins, описанную в Собственная документация TypeScript , вы можете сделать следующее:
class TwitterBase extends PostComponent {}
interface TwitterBase extends CanLike, CanDelete, CanShare {}
applyMixins(TwitterBase, [
shareMixin,
likeMixin,
deleteMixin
]);
После создания базового компонента, составленного с микшинами, мы можем расширить новый компонент TwitterPostComponent:
@Component({
selector: 'twitter-post',
template: `
{{ post.author }}
{{ post.content }}
`
})
export class TwitterPostComponent extends TwitterBase {}
Чтобы удалить функциональность удаления из компонентов твитов, нам не нужно много делать – мы просто удаляем микшин deletemixin из нашего класса:
const TwitterBase = likeMixin(
shareMixin(PostComponent)
)
);
Ловушки с использованием микшинов
Микшины великолепны, но они не непогрешимый инструмент. Пока я все еще предпочел бы микшины Множественное наследство , важно понять последствия использования этой техники.
Это React Blog Post Предоставляет отличное объяснение того, почему микшины больше не считаются лучшей практикой в React:
Миксеры создают неявные зависимости: микшины, которые вызывают методы на компоненты, ссылаются на свойство из компонента или компоненты, которые нуждаются
Микшины начинаются с тех пор, но со временем растут
Микшины приводят к столкновению имени
Конечно, из -за сходства они также применяются к микшинам TypeScript, используемым с угловыми компонентами.
Как избежать этих ловушек?
Старайтесь не наносить слишком много микшинов; Если у вас слишком много микшинов, возможно, вам следует разделить компонент на несколько компонентов и использовать компонент компонентов с входами и выходами для связи между собой
Стремитесь сохранить их максимально маленькими
Поддерживает зависимости между микшином/компонентом к минимуму. Например, везде, где это возможно, постарайтесь не вызывать зависимости компонента из Mixin
Объедините технику микшинов с компонент композиции. Вместе с использованием небольших микшинов вы можете использовать оба метода для обмена кодом и поддержания здоровой кодовой базы
Ресурсы
Угловой материал библиотека, которая использует микшины, поэтому я бы предложил вам проверить их компоненты, чтобы увидеть, как их можно использовать в различных ситуациях
Если вам нужны какие -либо разъяснения, или если вы думаете, что что -то неясно или неправильно, сделайте, пожалуйста, оставьте комментарий!
Надеюсь, вам понравилась эта статья! Если вы это сделали, следуйте за мной* Средний , Twitter или мой Веб -сайт Для получения дополнительных статей о разработке программного обеспечения, фронт -энд, RXJS, TypeScript и многое другое!
Оригинал: “https://dev.to/gc_psk/composing-angular-components-with-typescript-mixins-dn3”