Здравствуйте, IB, Вы писали:
IB>Здравствуйте, Vladek, Вы писали:
IB>Вот в этом и проблема такого подхода. Если объект user и письмо отправляет, и в БД пишет, то код который использует user может и не поменяется, зато код отравки письма может запросто поменять логику записи в БД, причем зачастую без ведома разработчика.
А еще можно вспомнить про версионирование интерфейса "обьекта юзер" и поддержку чтения-записи из-в бд разных версий этого обьекта
Ну там v1 — FirstName/LastName, v2 — FirstName/SecondName/LastName — ваще весело становится
V>Если IssueInvoice ломает поведение SendPromotion, то это просто ошибка программирования, которую надо исправить. Код, который использует класс User, меняться не будет — потому что все детали скрыты внутри класса User и фикс бага не выйдет за его пределы.
Дык все эти ооп, солиды и прочие граспы они специально придуманы для того, чтобы минимизировать вероятности появления "просто ошибок".
И тебе явно показали сценарии, в каких ДДД приводит к росту вероятности появления "просто ошибок" что есть плохо вообще в принципе.
А так можно сказать что фортран-стайл рулит. Если там чот не так — ет "просто ошибка, которую надо исправить".
PS
а ваще, DDD просто устарел. Но при этом сыграл фундаментальную роль в эволюции программирования.
Здравствуйте, Vladek, Вы писали:
V>Здесь надо разбираться с границами ответственности объекта User. Пользователи не участвуют в формировании счетов, они их только оплачивают и правила формирования счетов от них зависят только косвенно (как они воспользовались услугами или товарами). Поэтому метод ChargeUser будет использовать объект User, у которого вызовет метод — IssueInvoice, передав уже готовый счёт на оплату. Находиться метод ChargeUser будет в другом объекте — в какой-нибудь истории покупок.
Это не важно, тут хоть горшком назови. Важно, что в классе User смешиваются логики, которые не имеют никакого отношения друг к другу, и все они имеют доступ к внутреннему состоянию класса. Самое интересное, что доступ к этому внутреннему состоянию не нужен ни одному ни другому методу.
V>важно другое — пользователя класса User не должно беспокоить как его методы реализованы и как они друг на друга влияют, контракт использования класса от этого не зависит.
Вот как раз это и не важно. ) Важно что внеся эти методы внутрь класса, мы дали им возможность неявно влиять друг на друга. Какую проблему мы этим решили?
V> Если IssueInvoice ломает поведение SendPromotion, то это просто ошибка программирования, которую надо исправить.
Исправить ошибку дороже чем недопустить.
V> Код, который использует класс User, меняться не будет — потому что все детали скрыты внутри класса User и фикс бага не выйдет за его пределы. Вот это и есть правильная инкапсуляция.
Еще раз. Что SendPromo, что IssueInvoice — являются внешней логикой по отношению к User, а значит внося их внутрь класса и давая доступ к приватным данным класса, мы нарушаем инкапсуляцию этих приватных данных.
V>Логики списания денег там быть не должно, а весь остальной код манипулирования данными пользователей будет перед глазами в одном файле. Ну а раз весь код сосредоточен в одном месте, то и юнит-тесты писать легко и соответственно ошибки можно заметить быстрее.
Юнит-тест, по очевидным причинам, такую ошибку не выловит — оба метода могут быть написаны совершенно корректно с точки зрения своей логики.
А тот факт, что в одном месте собран как код отправки промо, так и выставления инвойсов, скорее усложнит задачу, так как при написании логики отправки сообщений придется думать не только о сообщениях, но и вообще обо всем, что только можно сделать с пользователем.
V>Ну вот выше ChargeUser (MonthlyCharge) мы вынесли за пределы класса User и я попытался объяснить почему ему там не место.
Все не так. Методу ChargeUser не место в классе User не из-за соображений текущей бизнес-логики, которое может меняться в зависимости от погодных условий, линии партии и мнения аналитика/архитектора, а по очень простому правилу "большего пальца". Если методу не нужен доступ к приватному состоянию класса, значит это внешний метод по отношению к классу.
Здравствуйте, itslave, Вы писали:
I>Здравствуйте, Vladek, Вы писали:
V>>Если IssueInvoice ломает поведение SendPromotion, то это просто ошибка программирования, которую надо исправить. Код, который использует класс User, меняться не будет — потому что все детали скрыты внутри класса User и фикс бага не выйдет за его пределы. I> I>Дык все эти ооп, солиды и прочие граспы они специально придуманы для того, чтобы минимизировать вероятности появления "просто ошибок". I>И тебе явно показали сценарии, в каких ДДД приводит к росту вероятности появления "просто ошибок" что есть плохо вообще в принципе.
Здравствуйте, IB, Вы писали:
IB>Еще раз. Что SendPromo, что IssueInvoice — являются внешней логикой по отношению к User, а значит внося их внутрь класса и давая доступ к приватным данным класса, мы нарушаем инкапсуляцию этих приватных данных.
Ты просто не понимаешь, что такое инкапсуляция. Могу только посоветовать читать Гради Буча. Добро пожаловать в таинственный мир ООП.
Здравствуйте, Vladek, Вы писали:
V>Ты просто не понимаешь, что такое инкапсуляция. Могу только посоветовать читать Гради Буча. Добро пожаловать в таинственный мир ООП.
Буча читать вредно. Возьми только его иерархию датчиков для теплицы — это же трипл фейспалс с современной точки зрения.
Здравствуйте, Vladek, Вы писали:
V>Ты просто не понимаешь, что такое инкапсуляция. Могу только посоветовать читать Гради Буча. Добро пожаловать в таинственный мир ООП.
Друг мой, к сожалению вы, похоже, дальше Буча не пошли...
Если уж у вас такой пиитет перед классиками — ссылку на Майерса я уже давал, но мне не сложно повторить:
Since then, I've been battling programmers who've taken to heart the lesson that being object-oriented means putting functions inside the classes containing the data on which the functions operate. After all, they tell me, that's what encapsulation is all about.
They are mistaken.
Вот это mistaken и про вас тоже.
А теперь контрольные вопросы.
— Какую проблему мы решаем, добавляя метод в класс?
— Какую проблему мы решаем, добавляя метод в класс, если методу не нужен доступ к приватным данным класса?
Здравствуйте, IB, Вы писали:
IB>А теперь контрольные вопросы. IB>- Какую проблему мы решаем, добавляя метод в класс?
Реализуем поведение объекта и манипулируем его полями, чтобы изменить состояние объекта.
IB>- Какую проблему мы решаем, добавляя метод в класс, если методу не нужен доступ к приватным данным класса?
Методам SendPromo и IssueInvoice не нужен конкретный пользователь и его данные? Покажи свой код, как бы ты решил проблему.
Здравствуйте, itslave, Вы писали:
I>Здравствуйте, Vladek, Вы писали:
V>>Методам SendPromo и IssueInvoice не нужен конкретный пользователь и его приватные данные . I>Я поправил пост за тебя.
Мне надоело переливать из пустого в порожнее с приверженцами процедурного программирования.
В моём коде пользователь сам себе отправляет промоушены и выставляет счета, потому что меня не интересует как он это делает. Я просто указываю ему что делать, используя его контракт (интерфейс). Ответ на вопрос как — целиком во власти объекта и вполне может поменяться в будущем, что однако не изменит контракта. Это ООП, который служит двум главным целям, ради которых код вообще пишется — код должен работать и быть готовым к изменениям.
В случае с простыми структурами данных и процедурами код позволяет поменять у двух пользователей адреса и послать им чужие промоушены, выставить чужие счета. Структуры данных не имеют идентичности (и поведения, ведь мы не любим методы, ко-ко-ко), это просто набор полей, которые легко превратить в мусор и скормить процедуркам, получив мусор на выходе. Контракт размыт и неявен.
Здравствуйте, Vladek, Вы писали:
V>Реализуем поведение объекта и манипулируем его полями, чтобы изменить состояние объекта.
Это не проблема, это задача.
V>Методам SendPromo и IssueInvoice не нужен конкретный пользователь и его данные?
Конкретный пользователь и внутреннее состояние объекта пользователь — разные вещи.
V>Покажи свой код, как бы ты решил проблему.
Какую? ) Я же просил сначала сформулировать проблему, которую вы хотите решить добавляя метод в класс.
Здравствуйте, Vladek, Вы писали:
V>Мне надоело переливать из пустого в порожнее с приверженцами процедурного программирования.
То есть нормальные аргументы закончились? ) И полегче с эпитетами, тут в целом уже понятно кто в догмах закостенел.
V>В моём коде пользователь сам себе отправляет промоушены и выставляет счета, потому что меня не интересует как он это делает.
Это заблуждение. Как я уже показал, при таком подходе как раз и получается, что вне зависимости от желаний придется разобраться что и как делает пользователь, в противном случае не избежать побочных эффектов.
Знаете почему глобальные переменные это плохо? Вот здесь та же история. Чем больше всякого разного с пользователем можно сделать, тем выше вероятность, что одно действие неявно повлияет на другое.
V> Я просто указываю ему что делать, используя его контракт (интерфейс). Ответ на вопрос как — целиком во власти объекта и вполне может поменяться в будущем, что однако не изменит контракта.
Контракта это конечно не изменит, но вот то что произойдет внутри контракта, превратится в конкретный головняк. И чем больше методов внутрь добавляется, тем больше бардака будет, хотя снаружи контракт вроде как чистенький.
V>Это ООП, который служит двум главным целям, ради которых код вообще пишется — код должен работать и быть готовым к изменениям.
Вы все напутали На самом деле, правило OCP звучит так: "software entities should be open for extension, but closed for modification". Ваш же подход очевидным образом этому противоречит. Если нам, допустим, понадобится помимо промо предложений рассылать еще и купоны на скидки, то следуя вашей логике нужно будет поместить метод SendDiscount в класс user, то есть модифицировать его (а это означает риск неявно изменить поведение класса). Тогда как можно было бы не трогать этот класс вообще, реализовав логику снаружи.
SRP, кстати, тоже нарушается — класс user отвечает за все что можно теперь и ни о какой Single Responsibility речь даже не идет. И Interface Segregation опять же мимо... Вообщем, вам похоже надо SOLID принципы в памяти оживить. )
V>В случае с простыми структурами данных и процедурами код позволяет поменять у двух пользователей адреса и послать им чужие промоушены, выставить чужие счета.
Это каким образом? Как я уже показал, код promo.SendPromo(user) гораздо меньше подвержен наведенным эффектам, чем user.SendPromo(), если, конечно, речь не об extension methods.
V> Структуры данных не имеют идентичности (и поведения, ведь мы не любим методы, ко-ко-ко), это просто набор полей, которые легко превратить в мусор и скормить процедуркам, получив мусор на выходе. Контракт размыт и неявен.
Во-первых, все-таки не структуры, а классы, во-вторых, идентичность-таки у них есть. А в третьих, кто их будет превращать в мусор и зачем? Здесь любое изменение объекта это явное действие в рамках конкретной логики, более того, эти классы/структуры вообще можно сделать immutable и тогда будет гарантия компилятора, что их никто и никогда не поменяет. Это, к слову, еще одно преимущество такого подхода.
А что касается контракта, то он более чем явен. Просто он в другом месте, он не вокруг данных, а вокруг собственно логики, бизнес-логики, если быть точным. И это намного более правильное место для контракта.
Здравствуйте, Vladek, Вы писали:
V>Здравствуйте, itslave, Вы писали:
I>>Здравствуйте, Vladek, Вы писали:
V>>>Методам SendPromo и IssueInvoice не нужен конкретный пользователь и его приватные данные . I>>Я поправил пост за тебя.
V>Мне надоело переливать из пустого в порожнее с приверженцами процедурного программирования.
V>В моём коде пользователь сам себеразные сервисы отправляют промоушены и выставляют счета, потому что меня не интересует как он это делает. Я просто указываю ему им — сервисамчто делать, используя егоих контракт (интерфейс). Ответ на вопрос как — целиком во власти объекта и вполне может поменяться в будущем, что однако не изменит контракта. Это ООП, который служит двум главным целям, ради которых код вообще пишется — код должен работать и быть готовым к изменениям.
Здравствуйте, itslave, Вы писали:
I>ASP.NET Core WebAPI — пока еще сыроватый кроссплатформенный продукт, развитие ASP.NET WebAPI и (почти) полностью с ним же совместимый. Если аффтар любит модное-молодежное, и вероятность попасть на косяки фрйемворка его нее смущает — то не вижу причин не использовать
Немножко не так.
Сперва народ писал REST на MVC. Потом некие перцы нашли в MVC фатальный недостаток и написали свой точно такой же, но с преферансом и женщинами, назвав его WebApi.
Потом они долго и упорно пытались сделать WebApi и MVC почти совсем совместимыми, но архитектурная стройность концепций танцорам мешала.
В это время я как раз ковырялся с REST на rsdn. Поглядев на все это ..., а так же по историческим причинам, на WebApi забил, написав простенький фреймворк для MVC, и ни разу, что характерно, об этом не пожалел.
В это времяч в МС нашли фатальный недостаток в WebApi. И решили новый WebApi реализовать в виде ... ага, дополнений к MVC. В этот раз архитектурная стройность концепций почему то уже не мешала.
Но таки появились новые архитектурные концепции, и они, конечно, тут же начали танцорам мешать. Вчера прям очередной пример накопал (вот ведь бараны упоротые) — https://github.com/aspnet/Mvc/issues/5507
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>