Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
data’ = A (data) я все-таки сдался. Он ведь пытается сделать очередный основанный на FRP подход/фреймворк, так?чувак звучал очень умно
пытается сделать очередный основанный на FRP подход/фреймворк
Dev — итак, на этом экране нужны данные x, y и z. Не мог бы ты сделать API, которое вернет данные в формате {x:, y:, z: }

Я больше не использую Wordpress — я могу получить лучшее от технологий HTML5 и CSS3 с тем же уровнем затрат (или меньшим).
Лично мне статья показалась скорее попыткой под другим углом взглянуть на MVC.
data̕ = 5; // символ "запятая сверху справа" - ̕
к примеру, cohesion и coupling.
разработчики употребляли термин «побочный эффект» или «сторонний эффект».
var s = { };
theme.slider(s);
console.log(s); // wat?!
function foo (x) {
x = { a: 10 };
}
var s = { a: 5 };
foo(s);
console.log(s.a); // 5
function foo (x) {
x.a = 10;
}
function foo(x) {
x.a = 10
}
var s = { a: 5 }
foo(s)
console.log(s.a)
"arg1_default"; function f(obj = {arg1:'default_val1', arg2:'default_val2'}){...}
function f(arg1='default_val1', arg2='default_val2', ...){...}
</perfection_mode>Чистая функция render в JSX — это квинтэссенция подхода PHP («отрисовать страницу и умереть, не заботясь о менеджменте ресурсов»)
Если есть архитектор, то решения принимаются быстро
Если у вас скрамный скрам с «равноправием»
плоская выдача вместо иерархической, апдейты диффами
{
"next" : { favorite : [ "mary" , "alice" ] },
"prev" : { favorite : [ "alice" ] }
}{
"next" : { favorite : [ "mary" , "alice" ] },
"prev" : {}
}{ "next" : {...} } вместо {...}, чтобы потом можно было легко прикрутить диффы, не ломая апи. :-D// GET /search=MVC/proj=habr?fetch=name,found(name,tag,prof(name))
{
"search=MVC/proj=habr" : {
"name" : "MVC",
"found" : [ "post=277113" , "user=lair" ]
},
"post=277113" : {
"name" : "Почему я больше не использую MVC-фреймворки",
"tag" : [ "tag=web-разработка", "tag=angularjs", "tag=javascript", "tag=patterns" ]
},
"user=lair" : {
"name" : "Сергей Роговцев",
"prof" : [ "prof=architect" ]
}
"prof=architect" : {
"name" : "Архитектор"
}
}uri в качестве идентификатора
в слегка извращенной форме
В данном случае используется relative-uri
found), и наверное имеется в виду, что это — внутренние связи через идентификаторы. (Но есть и аналогичные по виду ключи — теги — которые нигде в документе больше не используются. А что с ними делать?) Но машина-то не умеет смотреть прищурившись, ей все надо объяснять. И поэтому, чтобы работать с вашим форматом, нужен специально написанный прикладной API, который будет знать, что в блоке found лежат идентификаторы, по которым надо забрать блоки с уровня выше, а потом в каких-то из них повторить аналогичные процедуры.Под семантикой я понимаю, как ни странно, семантику — то, какой смысл вкладывается в элемент. В приведенном вами примере есть набор строковых ключей, за каждым из которых лежит какой-то объект. Все эти ключи — равноправны, и понять, чем они друг от друга отличаются, из самого документа — нельзя.
"status": "shipped" — это одно из значений перечисления [ "shipped", "processing", "cancelled" ], а не произвольная строка? Для передачи семантики, необходима расширенная система типов (именованное перечисление, значение в заданной валюте, ссылка на сущность определённого типа и тд). В то же время JSON имеет ограниченную систему типов (строка, число, словарь, список, флаг, пустота). HAL же вводит лишь один единственный дополнительный тип "список ссылок на другие сущности". То есть не решает проблему систематично, а не понятно зачем вставляет частичный костыль. В результате, вместо того, чтобы писать просто:{
"status" : "processing",
"items": [ "box=123" , "letter=321" ]
}status processing
items
box=123
letter=321{
"_links": {
"items": [{
"href": "box=123"
},{
"href": "letter=321"
}]
}
"status" : "processing",
}status из строкового типа в элемент перечисления и обратно.Но есть и аналогичные по виду ключи — теги — которые нигде в документе больше не используются. А что с ними делать?
// GET /search=MVC.proj=habr.type=post?fetch=name,found(name,tag(name,rating))
{
"search=MVC.proj=habr.type=post" : {
"name" : "MVC",
"found" : [ "post=277113" ]
},
"post=277113" : {
"name" : "Почему я больше не использую MVC-фреймворки",
"tag" : [ "tag=javascript", "tag=patterns" ]
},
"tag=javascript" : {
"name" : "javascript",
"rating" : 4
},
"tag=patterns" : {
"name" : "patterns",
"rating" : 5
},
}чтобы работать с вашим форматом, нужен специально написанный прикладной API, который будет знать, что в блоке found лежат идентификаторы, по которым надо забрать блоки с уровня выше, а потом в каких-то из них повторить аналогичные процедуры.
А уж догадаться, глядя на пример, что эти же идентификаторы можно впихнуть в запрос просто как часть пути — это отдельный челлендж.
И я, кстати, до сих пор не уверен, что то, как они преобразуются из относительных в абсолютные, — это то, что вы задумывали.
А как понять, что «status»: «shipped» — это одно из значений перечисления [ «shipped», «processing», «cancelled» ], а не произвольная строка?
Для передачи семантики, необходима расширенная система типов
HAL же вводит лишь один единственный дополнительный тип «список ссылок на другие сущности». То есть не решает проблему систематично, а не понятно зачем вставляет частичный костыль.
Это касается любого формата, будь то хоть HAL, хоть OData, хоть JSONAPI, — требуется адаптер, предоставляющий API для работы с протоколом.
Я вроде бы прямо сказал, что так и задумано. В соответствии со спецификацией.
search=MVC/proj=habr), не покидая контекст документа (/search=MVC/proj=habr), я попаду в /search=MVC/search=MVC/proj=habr, и так до бесконечности?Не вижу принципиальной разницы между «типизацией» и «семантикой» в данном контексте.
В том и проблема, что HAL — это костыль для частного случая.
А может это вы видите то, чего нет?
price и discount один и тот же тип (до тех пор, пока у вас не type-driven development, по крайней мере), но разная семантика.Решается выдачей схемы.
А типы просто гарантируют, что смысл не будет перепутан.
Не избыточно, а расширяемо. Вы как обычно переусложняете
{ _links": { "items": [{ "href": "box=123" }] } } — тут мы фактически вводим тип "ссылка", правда не конкретизируем ссылка это на какой тип. Просто динамическая ссылка на произвольную сущность.Если мы не вводим отдельные типы для price и discount то, очевидно, никак.
тут мы фактически вводим тип «ссылка», правда не конкретизируем ссылка это на какой тип
Это я вам как фронтенд разработчик заявляю — у нас есть острая необходимость знать схему запроса/ответа,
Тип — это формализованная семантика.
Ресурс какого-то типа
type/profile.Расширяемой разработчиком схемы, а не выбитой в граните спецификации как HAL.
Это от разработчика зависит а не языка.
Да, но это приходится указывать в каждой ссылке, вместо того, чтобы вынести это в схему.
HATEOAS — несколько неудобная реализация той же идеи.
Пользовательские типы есть везде. И в JS в том числе.
Чтобы она была полностью самодостаточна, можно тут же приложить ещё и содержимое ресурса, на который она ссылается.
Я и не утверждал, что у меня HATEOAS.
class Scalar
{
constructor( value )
{
this.value = value
}
static ensure( value )
{
if( value instanceof this ) return value
throw new Error( 'Wrong type' )
}
}
class Price extends Scalar {}
class Discount extends Scalar {}
class Order()
{
_price = null
get price()
{
return this._price
}
set price( value )
{
this._price = Price.ensure( value )
}
_discount = null
get discount()
{
return this._discount
}
set discount( value )
{
this._discount = Discount.ensure( value )
}
}
var order = new Order
order.price = new Price
order.discount = order.priceclass Order()
{
@Price.prop()
_price = null
@Discount.prop()
_discount = null
}class Order
{
@Price price = null
@Discount discount = null
}Вот, что я говорю: «HATEOAS — несколько неудобная реализация той же идеи.». Я не считаю необходимым ссылки на другие ресурсы выделять как-то особо, относительно остальных типов.
чтобы не раздувать объём выдачи
https://example.com/person={personId}?fetch={fetchPlan}. Я тут запилил пример того, то я имею ввиду, правда с XML выдачей, но формат легко меняется на любой другой: http://nin-jin.github.io/harp=1/
Он не масштабируется под реальные приложения как Дэн любит утверждать. Давайте рассмотрим простой пример, объясняющий почему.
Другими словами, машины состояния не должны быть кортежами, соединяющими два состояния (S1, A, S2) каковыми они обычно являются. Они являются, скорее, кортежами форм (Sk, Ak1, Ak2,...) которые определяют все разрешенные Actions для состояния Sk с результирующим состоянием вычисляемым после того, как Action был применен к системе и Model обработал изменения.
case INCREMENT:
return state + 1;
function increment(data) {
data.counter = data.counter || 0 ;
data.counter += 1 ;
return data ;
}
model.present = function(data) {
if (data.counter !== undefined) {
// can have some validation rules that decides whether
// that value is acceptable or not
model.counter = data.counter ;
}
}
case INCREMENT:
return state + 1;
function increment(data) {
data.counter = data.counter || 0 ;
data.counter += 1 ;
return data ;
}
model.present = function(data) {
if (data.counter !== undefined) {
// can have some validation rules that decides whether
// that value is acceptable or not
model.counter = data.counter ;
}
}
it does not have a «next-action-predicate», so when the counter is decremented and something has to decide what to do next, Redux, is powerless, some ugly code will show up in the reducer.
Redux is coupling action/model inside the reducer
All I am saying, you need to write an action increment:
В MVC такая логика будет реализована в Controller, и, вероятно, вызываться по таймеру из View.
In MVC, that kind of logic would be implemented in the controller, perhaps triggered by a timer in the view.
никаких проблем с сохранением состояния страницы после перезагрузки.
Не вижу ни малейших проблем с сохранением состояния
разумеется не в случае MVC
Именно эта проблема вынудила разрабов использовать клинтские фрейморки, асинхронные вызовы и прочую ересь что многократно усложнило разработку.
А к примеру в WebForms такой необходимости нет — чекер никуда не сбросится.
Нет, не эта — а желание ускорить отклик интерфейса на действия пользователя.
Никогда не задумывались, какой ценой?
а какие проблемы с откликом?
Что интернет медленее работает если webforms или база данных?
И как MVC ускоряет отклик?
нет никакой цены
а какие проблемы с откликом?А вы попробуйте использовать синхронные HTTP вызовы. Удивитесь.
А вот если не просто асинхронка а еще и модные нынче клиентские фреймворки с сотнями евентов и биндингов то отклик однозначно больше, особенно на мобильных браузерах.
try {
response = await myService.myAsyncCall()
} catch (e) {
// handle exception
}вообще то речь шла о клиенте.
если речь о сервере то в большинстве случаев ощутимого выиграша тоже нет.
данная конструкция исправляет кривую архитектуру IIS не более того.
вообще то смысл ноды изначально как раз однопоточная работа
любой сервлет работает в отдельном потоке в ява машине
как разработчик пробую и те и другие. разницы практически никакой.Практически?! Я как пользователь ненавижу таких разработчиков как вы.
вы удивитесь но на диалапе сейчас никто не сидит.У меня сейчас не самый худший интернет. Если посмотреть на загрузки на данной странице хабра, то одним из самых быстрых запросов окажется, например, ваша аватарка. 1.3кб за 45мс (403 ответ).
Конечно рядовой пользователь скорее всего и не заметит, что приложение не отвечает 45 миллисекунд после нажатия какой-либо кнопки. Но ощущения от приложения точно поменяются.
и как асинхронка ускорила бы загрузку страницы хабра?
то есть о персистентности ее элементов после перезагрузки.
что многократно усложнило разработку.
А на архитектуру не плевать.
в MVC я вынужден писать код по восстановлению его состояния на случай перезагрузки этой же страницы, например для вывода ошибки.
Который раз смотрю ваши коментарии и убеждаюсь, что тред с вашим коментом скатится в обсуждение какого-то древнего легаси с которым вы вынуждены работать. Что ж вы делаете то там?
Всегда считал что что MVC в вебе — как на корове седло
Нет ничего естественнее компонентного подход
Никаких API, никакой асинхронки
И уж тем более нет необходимости изобретать мудреные конструкции аналогичные приведенным в статье.
в исполнении Zend и иже с ним
К примеру десятки файлов в папке model стандартного фреймворка на самом деле не модели а DTO
модель в класическом MVC одна — например база данных. К примеру Word — класический MVC.
Нет. DTO не имеет поведения, а модели — имеют.
И где в ворде база данных?
в большинстве случаев там ПО ФАКТУ именно DTO
не теоретизируйте — просто посмотрите реализацию этих «моделей» в любом фреймворке.
Модель в принципе одна а не десятки.
модель в класическом MVC одна — например база данных.
на самом деле не модели а DTO
In traditional MVC, the action (controller) would call an update method on the model and upon success (or error) decide how to update the view. As he points out, it does not have to be that way, there is another equally valid, Reactive, path if you consider that actions should merely pass values to the model, regardless of the outcome, rather than deciding how the model should be updated.
I don't code for a living, so I am not the best developer, but people can get a sense of how the pattern works and that you can do the exact same thing as React + Redux + Relay with plain JavaScript functions, no need for all these bloated library (and of course you don't need GraphQL).
А я вроде бы конкретно пояснил что я делаю, еще в самом начале
я очевидно указал, что эволюция в статье от MVC к M-Action-State-V — не более чем иллюзия и самообман.
Мой коммент первого уровня в этом треде содержит строку описвающую мою модель:
Этого не достаточно?
Где вы увидели «поломанную ООП»?
С каких пор композиция/декомпозиция самих объектов, перестала быть ООП?
Т.е. вы отрицаете, что Dependency injection...
очевидно что код Controller'a общается изолированно с View и Model — и отвечает на внешние запросы.
Контроллер принимает входящие данные из вне + окружение.
Сопоставляет парамтеры окружения и вводные со своей картой (Action Map, url_routing — суть есть задачи Контроллера).
В соответствии с этим он делает запрос к соответсвующим методом объекта Model.
Получает данные (может принять какое-то промежуточное решение, например Модель сказала что данных пока нет)
Вызывает метод/методы объекта View с передачей ему данных в аргументы методов — который уже генерит их уже в ответе
Получает в ответе какое-то blob/text/html-тело ответа в виде уже отрендеренного шаблона и отправляет ответ во внешний мир
Я вам привел пример с web-реализацией, продиктованной особенностью HTTP.
для чистоты можно отдать отдельные данные окружения сразу View,
В моей вариации Controller
Т.к. шаг влево, шаг вправо — все на всё похоже.
под Model как-бы везде понимается Структура-Данных (БД + базовые функции ПО (API))
B все-таки под Model как-бы везде понимается Структура-Данных (БД + базовые функции ПО (API))
Точнее View и есть «ответ» на внешний запрос
И почему я не могу дать свою вариацию?
public ActionResult Render(args)
{
ActionMap(args);
var some_data = this.CModel.get_data(args);
var some_view = this.CView.Template(some_data);//я все-таки исхожу из того, что данные вам для чего-то нужны
return some_view;
}public ActionResult Render(args)
{
ActionMap(args);
var model = this.Domain.get_data(args);
var result = this.View.Template(model);
return result;
}Да, но вы упускаете момент, я не просто так "..." троеточия поставил.
Уточним, все же данные из объекта класса Модель, а потом отдает эти данные нужному методу Представления, который рендерит на основе их ответ. Представление ничего не знает о Модели в принципе. Я намеренно так обозвал переменные, чтобы заострить на этом момент.
Сначала — тот факт, что MVC все в одном слое и общаются между собой
а у меня не MVC
Потом, по приведенному псевдокоду — вы вынесли вердикт, что это классический такой MVC для Web, потом вот опять Model2...
В общем, на самом деле держать за «идею MVC» как какую-то особую идею — ровно нет никакого смысла.
я делал все примерн тоже самое
Пожалуй, лучшее MVC выглядит примерно так


то, что нарисовал это на самом деле обычный MVVM
Замените ViewModel на Controller — ни сути ни определения это не поменяет.
M <-> C <-> V = всё и точка — едем дальше.

C --> VА где хранить статусы, например номер текущей страницы пагинатора, в модели? или MVC для этого не подходит?
А где хранить статусы, например номер текущей страницы пагинатора, в модели? или MVC для этого не подходит?
Ещё Вопрос: кто должен отвечать за подготовку данных, например если перед выводом нужно отсортировать, или задекорировать/перевести язык значения?
Какая же это изоляция?
MV — в другом (и они не общаются напрямую).
Если они не общаются напрямую то они априори не могут быть в одном слое.
4.1.1 Model
The model is a non-visual object containing all the data and behaviour other than that
used for its presentation to the user (Fowler 2003). Model represents the data and
state (Freeman et al. 2004) of an application and the domain logic that manipulates it
(Buschmann et al. 1996).
4.3.1 Separated Presentation or Model-View
The most fundamental principle in the Smalltalk MVC and all the others that followed,
is to separate the elements seen on the screen i.e. presentation or user interface, and
the set of classes that are concerned with the core of the application, i.e. the domain
objects or data management (Potel 1996; Fowler 2003). Fowler (2006b) refers to this
as Separated Presentation, while Sanderson (2010) names it Model-View (figure 4-
2).
Another strict rule is visibility: the presentation can call the domain objects, but not
vice-versa. The domain objects should be completely self contained and unaware of
any presentations. This is in fact a form of a layered architecture. The visibility rule can
even be validated with build-time tools. (Fowler 2006b)
(б) поскольку и контроллер, и представление знают о модели, они оба живут в одном слое (иначе будет нарушено правило непересечения слоев)
могут обратиться к Domain Model
Итого: разложив все в три слоя и пользуясь правилом непересечения слоев — картина получается непротиворечивая.
Почему Model может туда обращаться, а находящиеся там же (где и Model) View и Controller — нет?
Вот следуя ему, View и Controller могут обратиться к Domain Model.
Итого: разложив все в три слоя и пользуясь правилом непересечения слоев — картина получается непротиворечивая.
Figure 4-1. Building blocks of the Model-View-Controller architectural pattern. Solid
lines represent direct connections, dashed lines represent indirect connections
Figure 5-1. The VisualWorks version of MVC, which separated the concepts of domain
model and application model. (Bower & McGlashan 2000)
не поделитесь подробностями своих наблюдений? Я серьезно.
Почему я больше не использую MVC-фреймворки