Каждый ModelView выступает в роли модели/контроллера для ведомых ModelView и в качестве отображения для владеющего ModelView. Часть логики может выноситься как в чистые Model, так и в чистые View, которые являются лишь вырожденными случаями ModelView.
$my_user_list $my_view
- \Owner ModelView
users? /$my_user
kids /
<= Row*0 $my_user_row
user <= 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 требует изменения только лишь его владельцев. ✅ Фрактальная структура легко масштабируется на приложения любого размера.
Модели и отображения пассивны, и не знают друг о друге - они управляются презентером, который выступает и в качестве посредника между ними.
// 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
}
✅ Легко добавлять новые отображения, не меняя модели. И наоборот. ✅ Изменение интерфесов модели или отображения требует изменения только лишь презентеров. ❌ Трёх слоёв слишком мало на больших масштабах. ❌ Для использования состояния одного презентера из другого необходимо искусственное вынесение его в модели.
Контроллер создаёт отображение, и говорит ему с какой моделью работать. Так же он обрабатывает все команды от пользователя, и управляет своими подопечными.
// 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]
}
}
✅ Отображение может использовать произвольные модели с тем же интерфейсом. ✅ Легко добавлять новые отображения, не меняя модели. И наоборот. ❌ Для отображения разных типов моделей необходимо дублировать код отображения. ❌ Изменение интерфейса модели требует обновления всех использующих её отображений и контроллеров. ❌ Трёх слоёв слишком мало на больших масштабах.
Отображения работают с моделями через посредников, которые трансформируют абстракции предметной области в абстракции отображения и обратно. ViewModel также выступает хранилищем состояния отображения, не связанного с предметной областью.
// 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
}
✅ Отображение может использовать произвольные вьюмодели. ✅ Легко добавлять новые отображения, не меняя ни модели, ни вьюмодели. ✅ Изменение интерфейса модели или отображения требует изменения только лишь вьюмодели. ✅ Одну и ту же вьюмодель можно шарить между несколькими отображениями. ❌ Для отображения разных моделей необходимо дублировать код отображения и вьюмодели. ❌ Трёх слоёв слишком мало на больших масштабах.
Код работы с моделями пишется прямо в отображении.
// View
function Task_list() {
return <ul>{
Task.list.map( task =>
<li><Task_row {task} /></li>
)
}</ul>
}
// Model
class Task {
static list = [] as Task[]
}
✅ Отображение может использовать произвольные модели. ✅ Легко добавлять новые отображения, не меняя модели. ❌ Для отображения разных моделей необходимо дублировать код отображения. ❌ Изменение интерфейса модели требует обновления всех использующих её отображений. ❌ Двух слоёв слишком мало на больших масштабах.
✅ Удобно из модели получать любые отображения. ❌ Добавление нового отображения требует изменения модели. ❌ Отображение полностью определяется одной основной моделью. ❌ Загрузка модели вытягивает по зависимостям и все её отображения. ❌ Двух слоёв слишком мало на больших масштабах.
Когда вы проверяете код своего коллеги не нужно требовать от него “идеального” кода.
ℹ️ Правило такое: вам следует апрувить изменения, если они улучшают в целом кодовую базу, даже если они не идеальны, ведь идеального кода не существует. Код можно сделать лучше чем он был до этого, но не идеальным.
Если вы увидели, какие-то небольшие помарки в коде, которые совершенно не критичны, то не нужно категорично требовать их исправления. Вы можете оставить комментарии, и оставить на усмотрение автора, необходимо ли вносить какие-то изменения.
ℹ️ В стандартах код ревью гугла, описано, что в таких ситуациях можно использовать префикс NIT, в комментариях к пул реквесту. NIT — сокращение от “nitpick” или “придираться”.
❓В каких ситуациях стоит использовать NIT
- Чтобы выразить своё мнение или личное предпочтение. Очень важно отличать такие комментарии, от фактов. Довольно часто ревьюеры выставляют собственное мнение как истину и единственно верное решение
- Чтобы указать автору на мелкие замечания, которые не обязательно исправлять
- В целях обучения, когда вы менторите начинающего разработчика и ваш комментарий носит чисто образовательный характер