Декомпозиция программных компонент

от автора

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

Так как одна и та же прикладная сущность встречается в приложении во многих местах, в разных контекстах, и должна иметь разное представление, то базовая декомпозиция заключается в выделении модели предметной области, которая является источником истины для всех мест её отображения. И тут начинаются нюансы..

? Обратите внимание, что далее стрелки показывают не движение данных, как их часто рисуют, а наличие знания одного компонента системы, как работать с другим. В пределе это знание выражается в полном контроле жизненного цикла: от создания, до уничтожения. Отсутствие же знания даёт независимость от конкретной реализации, а значит и возможность работать с разными реализациями.

Model-View

Модель знает как себя по разному представлять.

MV

MV

Пример

class User { // Model  _id: bigint _nickname: string  toString() { // View return 'user=' + this._id }  toJSON() { // View return { id: String( this._id ), name: this._nickname, } }  }

Особенности

✅ Удобно из модели получать любые отображения.

❌ Добавление нового отображения требует изменения модели.

❌ Отображение полностью определяется одной основной моделью.

❌ Загрузка модели вытягивает по зависимостям и все её отображения.

❌ Двух слоёв слишком мало на больших масштабах.

View-Model

Код работы с моделями пишется прямо в отображении.

VM

VM

Пример

// View function Task_list() { return <ul>{ Task.list.map( task => <li><Task_row {task} /></li> ) }</ul> }  // Model class Task { static list = [] as Task[] }

Особенности

✅ Отображение может использовать произвольные модели.

✅ Легко добавлять новые отображения, не меняя модели.

❌ Для отображения разных моделей необходимо дублировать код отображения.

❌ Изменение интерфейса модели требует обновления всех использующих её отображений.

❌ Двух слоёв слишком мало на больших масштабах.

Model-View-ViewModel

Отображения работают с моделями через посредников, которые трансформируют абстракции предметной области в абстракции отображения и обратно. ViewModel также выступает хранилищем состояния отображения, не связанного с предметной областью.

MVVM

Пример

// View <li class="User_card" model="User_card_model"> <img src={ image } /> <p>{ message }</p> </li>  // ViewModel class User_card_model { user = User.current get image() { return this.user.avatar } get message() { return this.user.nickname } }  // Model class User { avatar: string nickname: string static current = new User }

Особенности

✅ Отображение может использовать произвольные вьюмодели.

✅ Легко добавлять новые отображения, не меняя ни модели, ни вьюмодели.

✅ Изменение интерфейса модели или отображения требует изменения только лишь вьюмодели.

✅ Одну и ту же вьюмодель можно шарить между несколькими отображениями.

❌ Для отображения разных моделей необходимо дублировать код отображения и вьюмодели.

❌ Трёх слоёв слишком мало на больших масштабах.

Model-View-Controller

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

MVC

MVC

Пример

// Controller class Users_resource { GET() { return User.all.map( user_brief ) } }  // View function user_brief( user: User ) { return { id: user.guid, name: user.passport.name_full, } }  // Model class User {  static all = [] as User[]  guid: GUID passports: Passport[] resumes: Resume[]  get passport() { return this.passports[0] }  }

Особенности

✅ Отображение может использовать произвольные модели с тем же интерфейсом.

✅ Легко добавлять новые отображения, не меняя модели. И наоборот.

❌ Для отображения разных типов моделей необходимо дублировать код отображения.

❌ Изменение интерфейса модели требует обновления всех использующих её отображений и контроллеров.

❌ Трёх слоёв слишком мало на больших масштабах.

Model-View-Presenter

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

MVP

MVP

Пример

// Presenter class User_preview { user: User card = new Card({ image: ()=> this.user.avatar, message: ()=> this.user.nickname, color: ()=> this.user.skin.color, click: ()=> this.skin_change(), }) skin_change() { this.user.skin = Skin.random() } }  // View <div class="Card" onclick={click} style={{ background: color }}> <img src={ image } /> <p>{ message }</p> </div>  // Model class User extends Model { avatar: string nickname: string skin: Skin }

Особенности

✅ Легко добавлять новые отображения, не меняя модели. И наоборот.

✅ Изменение интерфесов модели или отображения требует изменения только лишь презентеров.

❌ Трёх слоёв слишком мало на больших масштабах.

❌ Для использования состояния одного презентера из другого необходимо искусственное вынесение его в модели.

ModelView Fractal

Каждый ModelView выступает в роли модели/контроллера для ведомых ModelView и в качестве отображения для владеющего ModelView. Часть логики может выноситься как в чистые Model, так и в чистые View, которые являются лишь вырожденными случаями ModelView.

MVF

MVF

Пример

$my_user_list $my_view - \Owner ModelView users? /$my_user kids / <= Row*0 $my_user_row user <= user* $my_user  $my_user_row $my_card - \Having ModevView user $my_user avatar => image nickname => message  $my_card $my_view - \View not Model kids / <= Image $my_image uri <= image \about:blank <= Message $my_text text <= message \  $my_user $my_model - \Model not View avatar? \ nickname? \

Особенности

✅ Каждый ModelView полностью контролирует внутренние ModelView и ничего не знает про внешние.

✅ Любой ModelView может шариться между разными другими ModelView на любом уровне композиции.

✅ Изменение интерфейса ModelView требует изменения только лишь его владельцев.

✅ Фрактальная структура легко масштабируется на приложения любого размера.

Выводы

$mol_view построен на идеях MVF, так как это наиболее простой паттерн декомпозиции, который легко масштабируется по мере потребности и хорошо разделяет разные уровни абстракции.

Ссылочки

Другие анализы
Следите за новостями
Обсуждайте наболевшее
Донатьте на печеньки


Актуальный оригинал на $hyoo_page.

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

Какой паттерн лежит в основе вашей архитектуры?

0% MV0
2.78% VM1
36.11% MVVM13
27.78% MVC10
11.11% MVP4
11.11% MVF4
11.11% Другой, нарисую в комментах4

Проголосовали 36 пользователей. Воздержались 17 пользователей.

ссылка на оригинал статьи https://habr.com/ru/articles/804193/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *