Оптимистическую добавил.
QueryBuilder и AR в отдельном пакете?
Re: QueryBuilder и AR в отдельном пакете?
Видимо за 20+ в сфере финансово-экономического ПО ничего не понял о его разработке
Ваш подход к декомпозиии известен под названием Rich Domain Model
У него, как у всего есть положительные стороны, а есть и отрицательные.
Из отрицательных назову:
1. работа с БД становится дико неэффективной, потому что обычно происходит вот это: Теперь можно спокойно делать таблицу в БД.
А для эффективной работы БД проектирование схемы БД, правил хранения и обмена персистентными данными нужно делать ПЕРЕД написанием кода, или одновременно, отбрасывая проектные решения по домену по критерию - такое будет дико просаживать БД и ничего с этим нельзя будет сделать. Настолько ПЕРЕД, что иногда кажется что это преждевременная оптимизация.
(Исключения из такого порядка проектирования вначале БД, а потом прикладной код - универсальные коробочные продукты, предназначенные для решений о которых разработчики таких продуктов и не догадываются на этапе разработки)
Беда с персистентными хранилищами любого типа что после написания кода работающего с данными перепроектировать будет очень трудозатрано
2. В сложных проектах вся простота потока выполнения внутри методов типа changeStatus быстро испаряется, когда предстоящие изменения требуется валидирировать, запрашивать дополнительные данные в зависимости от условий if и часто густо, и на php тоже, еще и асинхронно. Плюс выделять скоупы транзакций, обрабатывать откаты, о которых надо уведомить другие части системы.
Избавиться от проблемы конечно можно - как в Magento нередко виден результат избавления - конструкторы в которые инжектится с десяток сервисов
3. Чем чревато превращение ActiveRecord в сложный Domain Model - все знают Но точно та же проблема и с самим "чистым" Domain Model в сложных проектах: у какого объекта должен быть changeStatus который изменяет состояния других объектов? почему у него, а не у другого?
"Чистыми классами", "DTOшками" я и назвал то что вы написали - чистые Domain Model=сферические кони в ваккуме. Которые в сложной системе будут обвешаны кучей вещей, и зависимы от кучи вещей.
Например уже говорилось - да бизнес-логика в силу технологических причин начнет утекать в ХП. Как у джавистов есть шутка - был программистом на джава а стал спецом на PL/SQL. Это реальная ситуация в - сложных системах.
То есть - реальные сложные системы далеки от той красоты что пишут о них в книгах, или подобно вашему красивому примеру "сложного приложения".
Это хорошо.
Но вот что забыл спросить еще в предыдущем посте
А зачем при разработке конкретной фичи запускать все 300 юнит тестов?
Вы одновременно правите сотню файлов?
Но тогда вопрос - а как это у вас декомпозирована система что вам надо сразу столько править?
А если правите с десяток - то зачем вам запускать ВСЕ 300 юнит тестов?
Перед пушем - понятно, нужно запустить побольше тестов, а лучше все что есть Но обычно все что есть это уже счет на десятки минут, и все прогоняются уже автоматикой на сервере.
А во время разработки - зачем?
То есть - вы решили проблему с скоростью запуска для какого-то странного, нетипичного цикла разработки
И
если у вас уже для такого простого примера, который вы делаете требуется 300 юнит тестов, то что будет в реальной сложной системе?
и сколько времени будут бежать тысячи вот таких юниттестов?
Я не против юнит тестов.
Я против академической архитектурной астронавтики.
неважно каким способом пишите
Это уже следующий вопрос
стоимость поддержки тестов.
Это ведь тоже код проекта, и он тоже должен быть актуальным, рабочим
И эта стоимость в сложных проектах тоже становится настолько большой что начинают задумываться об экономии.
И периодически возникает необходимость и тесты рефакторить, не из-за скорости их работы, а потому что одна строчка в коде требует изменений в пяти тестах, да еще и нетривиальных изменений. И тогда команда взвывает - так мы что вообще пишем - программу или тесты???
но это уже такое, лирическое из практики на разных проектах
Не желайте странного, и не будет у вас головной боли чтобы достичь этого странного.
Тем более что окажется что оно вам и не нужно было, странное это.
Тем более что окажется что оно вам и не нужно было, странное это.
- BrusSENS
- Сообщения: 565
- Зарегистрирован: 2012.07.26, 06:51
- Откуда: Новороссийск
- Контактная информация:
Re: QueryBuilder и AR в отдельном пакете?
А, простите, зачем тестировать без базы Dm?
Это всё понятно, но разговор то про DM шёл. Причём тут юнит тесты и DM? Зачем тестировать DM без базы, объясните пожалуйста, не понимаю просто.
Правда? Буду знать, спасибо. Тем не менее, объясните, зачем тестировать DM без базы. Ну ей богу не могу понять.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Режим обслуживания сайта для Yii 2.x.x
- BrusSENS
- Сообщения: 565
- Зарегистрирован: 2012.07.26, 06:51
- Откуда: Новороссийск
- Контактная информация:
Re: QueryBuilder и AR в отдельном пакете?
Так а зачем в сущности пихать много бизнес логики? Можно в небольшие сервисы логику выносить, как например PasswordHasher и т.п. И будет сущность чистая, без лишних соплей. Получается к разработке подходим абы как, а потом пытаемся всё это тестировать. А такие сервисы можно тестировать независимо.
Просто я реально не понимаю, зачем например тестировать такой код:
Код: Выделить всё
class User extends ActiveRecord
{
public function changePassword(string $password, PasswordHasherInterface $hasher)
{
$this->password = $hasher->hash($password);
}
}
По сути писал я проекты, с такой реализацией и тестировать "AR сущности" не приходилось, ибо зачем, если там очевидный код, который по сути будет всегда работать.
Это я о чём? Да о том, что просто возмущает надуманная проблема с тестированием AR. Ведь его по сути тестировать нет смысла, если не пихать туда всё, что можно. Паттернизм - бич сегодняшнего PHP. Вроде паттерны знают, вроде применяют, вроде об архитектуре размышляют. Только архитектуры то нет. Есть паттернизм, а не реальное понимание проблем разработки софта.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Режим обслуживания сайта для Yii 2.x.x
Re: QueryBuilder и AR в отдельном пакете?
Здесь не соглашусь. AR обычно содержит бизнес-логику, в том числе код для генерации SQL-запросов. Это уже очень много и это нужно тестировать, в частности необходимо проверять правильно ли поменялись данные в нужных таблицах. сгенерировались ли события и т.д. и т.п.BrusSENS писал(а): ↑2019.08.14, 14:06 надуманная проблема с тестированием AR. Ведь его по сути тестировать нет смысла, если не пихать туда всё, что можно. Паттернизм - бич сегодняшнего PHP. Вроде паттерны знают, вроде применяют, вроде об архитектуре размышляют. Только архитектуры то нет. Есть паттернизм, а не реальное понимание проблем разработки софта.
Выносить все в сервисы - я считаю это плохая практика. Приводит к такому явлению как анемичные сущности == структуры данных, что сделает код ближе к процедурному стилю, а именно сервисы будут играть роль функций, сущности - структур данных. Для того чтобы избежать этого нужно стараться побольше бизнес-логики делать в сущностях, агрегатах.
P.S. Я сущности делаю с подключением внутри и иногда другими зависимостями - почти вся бизнес-логика в них помещается, сервисы почти пустые.
- BrusSENS
- Сообщения: 565
- Зарегистрирован: 2012.07.26, 06:51
- Откуда: Новороссийск
- Контактная информация:
Re: QueryBuilder и AR в отдельном пакете?
Пример можно? Не совсем понимаю, что подразумевается под "код для генерации SQL-запросов".anton_z писал(а): ↑2019.08.14, 16:39 Здесь не соглашусь. AR обычно содержит бизнес-логику, в том числе код для генерации SQL-запросов. Это уже очень много и это нужно тестировать, в частности необходимо проверять правильно ли поменялись данные в нужных таблицах. сгенерировались ли события и т.д. и т.п.
Я говорю именно о том, что не нужно пихать сложную логику в них. Если есть сложная логика, которую сложно тестировать - в отдельные сервисы. В остальном получается всегда так, что сущность имеет простые методы, которые просто нет смысла тестировать.anton_z писал(а): ↑2019.08.14, 16:39 Выносить все в сервисы - я считаю это плохая практика. Приводит к такому явлению как анемичные сущности == структуры данных, что сделает код ближе к процедурному стилю, а именно сервисы будут играть роль функций, сущности - структур данных. Для того чтобы избежать этого нужно стараться побольше бизнес-логики делать в сущностях, агрегатах.
Сущность она же на то и сущность. Вот возмём к примеру человека. Если у человека оторвалась пуговица, то что мы делаем? Правильно. Пришиваем. Но зачем человеку всегда ходить с иголкой внутри? Правильно, незачем. Посему и делаем передачу "иголки" только тогда, когда нужно, а не каждый раз при инициализации.
И да, зачем нужны пустые сервисы? Сервис же на то и сервис, что бы описывать логику решения определённой задачи.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Режим обслуживания сайта для Yii 2.x.x
Re: QueryBuilder и AR в отдельном пакете?
ООП как раз и придумано, чтобы пихать логику со стейтом в слабосвязанные объекты. А те самые паттерны и принципы придуманы, чтоб внешнюю связанность модулей уменьшить для более осознанного контроля сложности.
И получится не объект, а, как и сказали, структура с полями (как ассоциативный массив) с методом save(). Со сквозным сильносвязанным расшаренным стейтом без инкапсуляции.
Такой код тестировать незачем:
Код: Выделить всё
class User extends ActiveRecord
{
public function changePassword(string $password, PasswordHasher $hasher): void
{
$this->hash = $hasher->hash($password);
}
}
Код: Выделить всё
public function requestPasswordReset(ResetToken $token, \DateTimeImmutable $date): void
{
if (!$this->isActive()) {
throw new DomainException('User is not active.');
}
if (!$this->email) {
throw new DomainException('Email is not specified.');
}
if ($this->resetToken && !$this->resetToken->isExpiredTo($date)) {
throw new DomainException('Resetting is already requested.');
}
$this->resetToken = $token;
$this->recordEvent(new PasswordResetRequested($this->id, $token));
}
Код: Выделить всё
public function makeBid(Member $member, Price $price, DateTimeImmutable $date): void
{
if (!$this->isActive()) {
throw new \DomainException('Lot is not active.');
}
if ($member === $this->member) {
throw new \DomainException('Unable to make bid by owner.');
}
foreach ($this->bids as $bid) {
if ($bid->isByMember($member)) {
throw new \DomainException('Bid is already placed.');
}
}
if ($this->bids->count() && !$price->isGreaterThan($this->getMaxBidPrice())) {
throw new \DomainException('Price must be greater than the previous one.');
}
if (!$this->bids->count() && !$price->isGreatherOrEqual($this->startPrice)) {
throw new \DomainException('Price must be greater or equal to start one.');
}
$bid = new Bid($this, $member, $price, $date);
$this->bids->add($bid);
$this->recordEvent(new BidCreated($this, $bid));
if ($this->blitzPrice && $price->isGreaterOrEqual($this->blitzPrice)) {
$this->close($date);
}
}
Код: Выделить всё
class Bid\Make\Handler
{
public function handle(Command $command): void
{
$lot = $this->lots->get(new Lot\Id($command->lot));
$member = $this->members->get(new Member\Id($command->member));
$lot->makeBid($member, new Price($command->price), new DateTimeImmutable());
$this->flusher->flush($lot);
}
}
Если вся логика вписана в сервисы, то и тестируем сервисы интеграционно.
Если же логика инкапсулирована в сущностях, то тестируем сущности юнитами.
Если вся логика для вписана прямо в БД хранимками, триггерами и констрейнтами без богатых сущностей, то ORM не нужен.
Если же логика для разделения вынесена в PHP-объекты, а БД используется как голое хранилище, то ORM нужен.
Всё логично.
Так и получается с AR "из коробки" в большинстве проектов, что тонны кода уже написаны, а тестировать его уже и лениво, и геморройно.
В этом и суть, что пытаемся тестировать "потом", а не "сначала". Как "семь раз отрежь, один отмерь".
- BrusSENS
- Сообщения: 565
- Зарегистрирован: 2012.07.26, 06:51
- Откуда: Новороссийск
- Контактная информация:
Re: QueryBuilder и AR в отдельном пакете?
Ни о какой "слабосвязаности" про ооп не говорится, не нужно выдумывать. ООП - всего-лишь подход для представления всего в виде объектов. И паттерны придуманы не для создания "слабой связанности", а для решения определённых проблем при проектировании. Где у нас синглтон связанность уменьшает? Верно, нигде. Это всего лишь решение, для того, что бы объект класса существовал в единственном экземпляре.
Я не в коем случае не говорил о том, что нужно пихать всё в отдельные классы. Нет. Просто считаю, что в сущностях сложной логики не должно быть. Сущность должна быть легковесной, понятной и самодостаточной.
И зачем? Что тут может пойти не так? А вообще это почему у нас этим должна заниматься сущность? Сколько говорили о том, что валидацией сущность не должна заниматься, а в итоге делаем всё наоборот. Это дело сервиса, но никак не сущности.ElisDN писал(а): ↑2019.08.14, 19:48 А в таком уже появляется зачем:
Код: Выделить всё
public function requestPasswordReset(ResetToken $token, \DateTimeImmutable $date): void { if (!$this->isActive()) { throw new DomainException('User is not active.'); } if (!$this->email) { throw new DomainException('Email is not specified.'); } if ($this->resetToken && !$this->resetToken->isExpiredTo($date)) { throw new DomainException('Resetting is already requested.'); } $this->resetToken = $token; $this->recordEvent(new PasswordResetRequested($this->id, $token)); }
Верно. Грустно. Потому что этого тут не должно быть. Опять валидируем? В сервисе проверили и сохранили, если нужно. Не место этому в сущности.ElisDN писал(а): ↑2019.08.14, 19:48 А в такой уже без тестов соседа впускать грустно:
Код: Выделить всё
public function makeBid(Member $member, Price $price, DateTimeImmutable $date): void { if (!$this->isActive()) { throw new \DomainException('Lot is not active.'); } if ($member === $this->member) { throw new \DomainException('Unable to make bid by owner.'); } foreach ($this->bids as $bid) { if ($bid->isByMember($member)) { throw new \DomainException('Bid is already placed.'); } } if ($this->bids->count() && !$price->isGreaterThan($this->getMaxBidPrice())) { throw new \DomainException('Price must be greater than the previous one.'); } if (!$this->bids->count() && !$price->isGreatherOrEqual($this->startPrice)) { throw new \DomainException('Price must be greater or equal to start one.'); } $bid = new Bid($this, $member, $price, $date); $this->bids->add($bid); $this->recordEvent(new BidCreated($this, $bid)); if ($this->blitzPrice && $price->isGreaterOrEqual($this->blitzPrice)) { $this->close($date); } }
Не всегда можно тесты писать до написания рабочего прототипа. Мы не в идеальном мире всё-таки живём Посему не всегда TDD подходит.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Режим обслуживания сайта для Yii 2.x.x
Re: QueryBuilder и AR в отдельном пакете?
У меня в одной CRM есть запросы(заявки) от клиентов, которые в приложении представлены классом common\models\Request.
Для заявок согласно заданию предусмотрено поднятие в списке заявок операторов по расписанию, которое задается оператором через gui или автоматически при наступлении определенных условий (здесь это не так важно). Здесь представлен метод, выполняющий поднятие заявки по расписанию. В случае ошибки или успеха, согласно бизнес-правилу необходимо внести запись в журнал поднятий.
CRM под конкретного клиента, поэтому сообщения на русском, смысла нет их в Yii::t() выносить.
Код: Выделить всё
class Request
{
/**
* @param RequestLiftSchedule $schedule
*
* @throws \Throwable
*/
public function lift(RequestLiftSchedule $schedule)
{
self::getDb()->transaction(function () use ($schedule) {
$lifted_at = date('Y-m-d H:i:s');
if (in_array($this->internal_status, [
self::INTERNAL_STATUS_OPENED,
self::INTERNAL_STATUS_CALL_FAILED
])) {
$this->lifted_at = $lifted_at;
if ( ! $this->save()) {
if ($this->hasErrors()) {
$error = array_values($this->getFirstErrors())[0];
} else {
$error = 'Не удалось обновить дату поднятия заявки по неизвестной причине.';
}
throw new \DomainException($error);
}
$comment = 'Успешно';
} else {
$comment = "Заявка в статусе \"{$this->internalStatusName()}\"";
}
$this->link('liftLogs', new RequestLiftLog([
'lift_at' => $schedule->lift_at,
'lifted_at' => $lifted_at,
'comment' => $comment
]));
$schedule->delete();
});
}
}
Под кодом, отвечающим за генерацию SQL-запросов имеется ввиду вызовы save(), link(), которые генерируют и выполняют sql-запросы.
Тут я согласен с @ElisDN, такому место в сущностях. Я бы почти также сделал, только у меня внутри метода были бы вызовы save()/update() и транзакция.
P.S. В целом по теме согласен со @skynin что php в оригинале больше годен для прямого общения с БД, чем для "синхронизации состояния" (flush) как с EnityManager. Это больше годно для сред, где процессы постоянно запущены, и то надо пробовать. Я и на C++ и на Java также как в примере делал, правда опыта у меня в этих средах маловато.
- BrusSENS
- Сообщения: 565
- Зарегистрирован: 2012.07.26, 06:51
- Откуда: Новороссийск
- Контактная информация:
Re: QueryBuilder и AR в отдельном пакете?
Вот именно этот код можно прекрасно вынести в отдельный сервис. Сущность от такого перегружается, зачем этому быть в сущности? По сути у нас сущность начинает выполнять ещё и роль фабрики, создавая RequestLiftLog.anton_z писал(а): ↑2019.08.15, 02:34Валидация AR здесь используется для проверки корректности данных правилам БД (а не для формы), чтобы туда что-попало не записалось. Это позволяет делать меньше VO, использовать скалярные типы. Вообще привычка на save() всегда такие проверки делать.Код: Выделить всё
class Request { /** * @param RequestLiftSchedule $schedule * * @throws \Throwable */ public function lift(RequestLiftSchedule $schedule) { self::getDb()->transaction(function () use ($schedule) { $lifted_at = date('Y-m-d H:i:s'); if (in_array($this->internal_status, [ self::INTERNAL_STATUS_OPENED, self::INTERNAL_STATUS_CALL_FAILED ])) { $this->lifted_at = $lifted_at; if ( ! $this->save()) { if ($this->hasErrors()) { $error = array_values($this->getFirstErrors())[0]; } else { $error = 'Не удалось обновить дату поднятия заявки по неизвестной причине.'; } throw new \DomainException($error); } $comment = 'Успешно'; } else { $comment = "Заявка в статусе \"{$this->internalStatusName()}\""; } $this->link('liftLogs', new RequestLiftLog([ 'lift_at' => $schedule->lift_at, 'lifted_at' => $lifted_at, 'comment' => $comment ])); $schedule->delete(); }); } }
Под кодом, отвечающим за генерацию SQL-запросов имеется ввиду вызовы save(), link(), которые генерируют и выполняют sql-запросы.
Тем не менее это совершенно не аргумент, т.к. всё приведённое ElisDN не должно находиться в сущностях, как и в приведённом Вами примере всё это вообще должно быть в сервисе. Просто аргументов по поводу того, что это должно быть в сущности я реально не увидел.
С++ по сути своей ничем не отличается от PHP, разве что может всё хранить в памяти, но там другая цена этому. Сборка мусора руками, утечки памяти и т.п. В каждом ЯП приходится платить за его преимущества. Но. PHP прекрасно справляется со своими задачами. На нём можно писать всё то, о чём уже тут говорили. Момент лишь в том, что я вижу, как говорят о том, что сущность не должна ничего валидировать, лишнего в ней не должно быть, а в итоге к ней прилепляют тот функционал, который её усложняет. И так не только с сущностями. Это касается всего, что пишется под лозунгом "True Architect". По сути все умных книг поначитались и суют эту практику в массы, а практика то ни о чём.anton_z писал(а): ↑2019.08.15, 02:34 P.S. В целом по теме согласен со @skynin что php в оригинале больше годен для прямого общения с БД, чем для "синхронизации состояния" (flush) как с EnityManager. Это больше годно для сред, где процессы постоянно запущены, и то надо пробовать. Я и на C++ и на Java также как в примере делал, правда опыта у меня в этих средах маловато.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Режим обслуживания сайта для Yii 2.x.x
Re: QueryBuilder и AR в отдельном пакете?
Это не валидация ввода из формы, а инкапсулированное поведение с контролем инварианта жизненного цикла объекта.
Давайте вместо того, чтобы придумывать свои извращённые термины, вы пойдёте и прочитаете оригинальное определение ООП от его автора Алана Кея. О том, что под ООП подразумевается система с обменом сообщениямими между самодостаточными объектами, инкапсулирующими в себе все состояние и логику для избавления от расшаренного состояния и для уменьшения связности. Вот тогда и будем спорить с вами о реальном ООП, а не вами придуманном.
Как видим по определению - это аргумент. Обеспечение инкапсуляции и снижение связанности с другими классами.
ООП позволяет мне сделать самодостаточный объект, живущий своей жизнью и контролирующий своё состояние. Хранящий всё надёжно внутри и не позволяющий присвоить ерунду в публичное поле снаружи.
У вас же вместо объекта есть просто беззащитная сущность (структура) с состоянием без поведения и пачка сервисов (процедур) с поведением без состояния. И кто угодно может присвоить что угодно без всякого контроля. Это процедурное программирование на классах, которое к объектно-ориентированному никакого отношения не имеет.
Так что нравится выносить в сервисы - выносите. Только тогда не называйте это объектно-ориентированным кодом.
Одиночка, фабрика, построитель - это порождающие паттерны, позволяющие сделать систему независимой от создания объектов. Отвязать от вызова конструктора. Вместо хардкорного создания нужного объекта через new мы его создаём или достаём из метода make() фабрики, build() построителя, copy() прототипа или instance() синглтона. А синглтон по сути является простой фабрикой с кешированием.BrusSENS писал(а): ↑2019.08.14, 22:07 И паттерны придуманы не для создания "слабой связанности", а для решения определённых проблем при проектировании. Где у нас синглтон связанность уменьшает? Верно, нигде. Это всего лишь решение, для того, что бы объект класса существовал в единственном экземпляре.
Структурные паттерны вроде декоратора и прокси как раз развязывают код, позволяя абсолютно незаметно модифицировать взаимодействие между объектами, внедряя прослойки между ними. Адаптер и фасад позволяют вообще не думать о стороннем объекте, полностью отвязав свой код от устройства сторонних компонентов.
Поведенческие паттерны вроде стратегии позволяют инкапсулировать фрагменты поведения, отвязав изменяемую логику от постоянной. Команда позволяет инкапсулировать алгоритм действия с зависимостями прямо в объекте команды. Посетитель позволяет посещать приватные внутренности коллекций независимыми посетителями и без необходимости нарушать инкапсуляцию, делая внутренности публичными. Посредник предоставляет связывать модули системы через себя без необходимости связывать их напрямую друг с другом.
Принципы SOLID выведены как рекомендации для написания слабосвязанных систем с разделением по изменяемым областям (S), с возможностью модификации без необходимости влазить внутрь (O), с соблюдением публичных контрактов (L) минимально необходимого размера (I) без завязки на реализацию (D).
Паттерны GRASP (общие шаблоны распределения ответственностей) целиком выведены именно для снижения Coupling между модулями и повышения Cohesion внутри модуля.
Как раз по принципу Information Expert из GRASP я изначально помещаю логику максимально внизу рядом с нужными ей данными. Метод makeBid прямо в сущности работает с её приватным состоянием. И мне не нужно открывать поля наружу для публичного доступа и искать, кто какое поле модифицирует. Если нужен какой-то сложный хелпер, то он передаётся в метод. Если же какой-то логике нужно управлять несколькими объектами А и B, то только она поднимается вверх в сервис С и оттуда дёргает A и B.
Так что практически все паттерны и принципы борются со связанностью, пытаясь развязать наш код: разделяют по ответственностям, выносят меняющиеся фрагменты, прячут сложные вещи, снижают привязку к сторонним системам, защищают от жёстких привязок модулей друг на друга, разбивают на слабосвязанные подсистемы.
Как видим, самый распространённый мегабич - это непонимание ООП как с вами в примере выше. И из него уже следуют остальные.
- BrusSENS
- Сообщения: 565
- Зарегистрирован: 2012.07.26, 06:51
- Откуда: Новороссийск
- Контактная информация:
Re: QueryBuilder и AR в отдельном пакете?
Называть можно как угодно, но у вас производится валидация (проверка данных на соответствие и допустимость их дальнейшего использования).
Читаем теперь внимательноElisDN писал(а): ↑2019.08.15, 15:21 Давайте вместо того, чтобы придумывать свои извращённые термины, вы пойдёте и прочитаете оригинальное определение ООП от его автора Алана Кея. О том, что под ООП подразумевается система с обменом сообщениямими между самодостаточными объектами, инкапсулирующими в себе все состояние и логику для избавления от расшаренного состояния и для уменьшения связности. Вот тогда и будем спорить с вами о реальном ООП, а не вами придуманном.
Как видим по определению - это аргумент. Обеспечение инкапсуляции и снижение связанности с другими классами.
ООП позволяет мне сделать самодостаточный объект, живущий своей жизнью и контролирующий своё состояние. Хранящий всё надёжно внутри и не позволяющий присвоить ерунду в публичное поле снаружи.
У вас же вместо объекта есть просто беззащитная сущность (структура) с состоянием без поведения и пачка сервисов (процедур) с поведением без состояния. И кто угодно может присвоить что угодно без всякого контроля. Это процедурное программирование на классах, которое к объектно-ориентированному никакого отношения не имеет.
Так что нравится выносить в сервисы - выносите. Только тогда не называйте это объектно-ориентированным кодом.
и перестаём приводить в аргументы из статей некачественного хостера на хабре. PHP не единственный ЯП. Может C++ тоже многие разработчики не понимают суть ООП?
Это ваши мысли сугубо личные. О том, что паттерны созданы для того, что бы сделать систему слабосвязанной - бред сивой кобылы. Нигде ничего подобного не говорится и намёков нет. Монолит тоже может быть качественным. А паттерны - всего лишь решения для определённого круга обыденных проблем, но никак не для борьбы со связанностью.ElisDN писал(а): ↑2019.08.15, 15:21 Так что практически все паттерны и принципы борются со связанностью, пытаясь развязать наш код: разделяют по ответственностям, выносят меняющиеся фрагменты, прячут сложные вещи, снижают привязку к сторонним системам, защищают от жёстких привязок модулей друг на друга, разбивают на слабосвязанные подсистемы.
А с чего Вы решили, что я понимаю неверно принципы ООП, а не Вы? Получается вот и аргумент. Собственное ЧСВ, ничего более. Аргументов nullptr. Вижу, что разговор зашёл в никуда и, думаю, что лучше каждый останется при своём мнении.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Режим обслуживания сайта для Yii 2.x.x
Re: QueryBuilder и AR в отдельном пакете?
Называйте как угодно. Хоть валидацией, хоть прекондишенами, хоть ещё как-нибудь. Но всё равно это реализация бизнес-правил. Но тогда кто вам сказал, что в объекте такой валидации быть не должно?
Не нравится перевод цитат на хабре - смотрите оригинал и читайте ответ самого Кея.BrusSENS писал(а): ↑2019.08.15, 19:15 Читаем теперь внимательно
и перестаём приводить в аргументы из статей некачественного хостера на хабре.
Если считаете википедию достоверным источником с определением от Ганди Буча, то промотайте до раздела Сложности определения и увидите ответ:
Так что хоть какое современное определение читайте, но изначально ООП придумывался для обмена сообщениями и работал идеально хоть по той же Actor Model в Smalltalk.ООП имеет уже более чем сорокалетнюю историю, но, несмотря на это, до сих пор не существует чёткого общепринятого определения данной технологии. Основные принципы, заложенные в первые объектные языки и системы, подверглись существенному изменению (или искажению) и дополнению при многочисленных реализациях последующего времени.
А PHP или C++ - не важно, как написано ниже. ООП работает и в JavaScript без классов (которые появились в 2015 чисто как синтаксический сахар) на утиной типизации, инкапсуляции замыканиями и прототипном наследовании. И также организуется в Lisp без классов на функциях с мутаторами.
Ага, конечно, не говорится. Заходим в вашу любимую Википедию и читаем сивую кобылу:BrusSENS писал(а): ↑2019.08.15, 19:15 Это ваши мысли сугубо личные. О том, что паттерны созданы для того, что бы сделать систему слабосвязанной - бред сивой кобылы. Нигде ничего подобного не говорится и намёков нет. Монолит тоже может быть качественным. А паттерны - всего лишь решения для определённого круга обыденных проблем, но никак не для борьбы со связанностью.
Порождающие шаблоны (англ. Creational patterns) — шаблоны проектирования, которые абстрагируют процесс инстанцирования. Они позволяют сделать систему независимой от способа создания, композиции и представления объектов.
Какие-то паттерны придуманы для этого явно. В других мы получаем уменьшение связанности неявно как бонус.Посредник (англ. Mediator) — поведенческий шаблон проектирования, обеспечивающий взаимодействие множества объектов, формируя при этом слабую связанность и избавляя объекты от необходимости явно ссылаться друг на друга.
Не используете инкапсуляцию, называете структуры без поведения объектами, путаете клиентскую валидацию с соблюдением инварианта. Это никак не вяжется с:
Так что оставайтесь при своём мнении сколько угодно долго. Реальность от этого не изменится.В центре ООП находится понятие объекта. Объект — это сущность, которой можно посылать сообщения и которая может на них реагировать, используя свои данные. Объект — это экземпляр класса. Данные объекта скрыты от остальной программы. Сокрытие данных называется инкапсуляцией.
- BrusSENS
- Сообщения: 565
- Зарегистрирован: 2012.07.26, 06:51
- Откуда: Новороссийск
- Контактная информация:
Re: QueryBuilder и AR в отдельном пакете?
А кто Вам сказал, что должна быть? Сущность, по своему определению не должна валидировать данные, разве не так?
Кей один из начинателей, не более. Да, многое внёс в понятие ООП, но есть и другие, кто говорит иначе.ElisDN писал(а): ↑2019.08.15, 23:43 Не нравится перевод цитат на хабре - смотрите оригинал и читайте ответ самого Кея.
Гради Буч, на минутку.
Об этом Вам и пытаюсь донести. Нет чёткого понимания, что такое true ООП. Посему и имею своё мнение, что сущность не должна проверять/валидировать корректность данных внутри себя. Или Ваше мнение только верное? Тогда у Вас ЧСВ, что поделать.ElisDN писал(а): ↑2019.08.15, 23:43Так что хоть какое современное определение читайте, но изначально ООП придумывался для обмена сообщениями и работал идеально хоть по той же Actor Model в Smalltalk.ООП имеет уже более чем сорокалетнюю историю, но, несмотря на это, до сих пор не существует чёткого общепринятого определения данной технологии. Основные принципы, заложенные в первые объектные языки и системы, подверглись существенному изменению (или искажению) и дополнению при многочисленных реализациях последующего времени.
А PHP или C++ - не важно, как написано ниже. ООП работает и в JavaScript без классов (которые появились в 2015 чисто как синтаксический сахар) на утиной типизации, инкапсуляции замыканиями и прототипном наследовании. И также организуется в Lisp без классов на функциях с мутаторами.
Верно, но...
Говорится про некоторые, но никак не про все, как Вы утверждали ранее.
Вполне вяжется. Написано же, сущность может реагировать, используя свои данные. Свои, а не обрабатывая другие сущности.ElisDN писал(а): ↑2019.08.15, 23:43 Не используете инкапсуляцию, называете структуры без поведения объектами, путаете клиентскую валидацию с соблюдением инварианта. Это никак не вяжется с:
В центре ООП находится понятие объекта. Объект — это сущность, которой можно посылать сообщения и которая может на них реагировать, используя свои данные. Объект — это экземпляр класса. Данные объекта скрыты от остальной программы. Сокрытие данных называется инкапсуляцией.
Реальность чья? Ваша? Конечно не изменится. ЧСВ тяжело лечить в общем. Вместо конкретики от Вас увидел лишь обобщённо один смысл: "Мой ООП верный. Я всё понимаю правильно, а вы, смертные, лучше слушайте, что я говорю.".
Посему давайте прекратим этот бессмысленный балаган, ибо ничего толкового из этого разговора не получается.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Режим обслуживания сайта для Yii 2.x.x
Re: QueryBuilder и AR в отдельном пакете?
Все определения ООП (старые и новые) включают в себя инкапсуляцию для обеспечения соблюдения инварианта объекта. Вынос проверок наружу дважды ломает инкапсуляцию и позволяет нарушить инвариант. Сущность становится несамостоятельной, так как теперь не может работать полноценно в одиночку без сервиса.
Скопируете сюда своё определение без инкапсуляции и валидации бизнес-правил – посмотрим.
Re: QueryBuilder и AR в отдельном пакете?
холивары про правильное/неправильное ООП обычно вызваны путаницей в головах о букве П.ElisDN писал(а): ↑2019.08.17, 09:32 Все определения ООП (старые и новые) включают в себя инкапсуляцию для обеспечения соблюдения инварианта объекта. Вынос проверок наружу дважды ломает инкапсуляцию и позволяет нарушить инвариант. Сущность становится несамостоятельной, так как теперь не может работать полноценно в одиночку без сервиса.
В одних случаях она расшифровывается как Проектирование, а в других как Программирование.
Причем, незаметно для себя эту путаницу нередко генерирует один и тот же человек.
Например в одном предложении пишет о программировании
"для обеспечения соблюдения инварианта объекта"
а потом уже о проектировании
"Сущность становится несамостоятельной"
Объект в ЯП, и сущность в домене - тождественные понятия, синонимы?
Для джунов у меня есть краткое замечание для напряжения мозга о разнице между ОО проектированием, и ОО программированием:
Представть язык программирования, у которого нет ни классов, ни объектов, а только интерфейсы. (впрочем уже представлять не сложно, берем Go)
И вот, для такого языка мы можем спокойно проектировать в ОО парадигме, но не можем и ОО строчки написать.
Итак, вам вопрос, к чему относится:
так как теперь не может работать полноценно в одиночку без сервиса - к проектированию (доменной области) или к программированию (реализации)?
И, случайно вышло что наткнулся на коменты:
Sam (ну который Dark)
- Как с высокими нагрузками справляется Doctrine ORM?
По сравнению с Yii — плохо.
- С Doctrine у меня такого опыта нет, но раз нужно хорошо знать SQL, EXPLAIN, то я так понимаю, что очень много SQL пишут руками, не полагаясь на QueryBuilder?
Индексы часто забывают плюс доктрина иногда чудит с построением запросов и да, приходится руками переделывать.
Symfony, по сравнению с Yii, работает медленней, но, в общем, терпимо.
C Doctrine я не работал. Но очень плотно работал с Hibernate, чтобы уверенно сказать
Такие ORMы нужны если пытаться отождествить ОО проектирование и ОО программирование, стереть разницу между ними.
И это - можно сделать, и будет полезно... при многих и всяческих если
Упоротость начинается тогда, когда сторонник, поклонник не понимает что "серебрянной пули" не существует. и выдает свои вкусы, предпочтения за единственно верный путь, трактовку
Не желайте странного, и не будет у вас головной боли чтобы достичь этого странного.
Тем более что окажется что оно вам и не нужно было, странное это.
Тем более что окажется что оно вам и не нужно было, странное это.
- samdark
- Администратор
- Сообщения: 9489
- Зарегистрирован: 2009.04.02, 13:46
- Откуда: Воронеж
- Контактная информация:
Re: QueryBuilder и AR в отдельном пакете?
ElisDN
Чтиво в тему: https://martinfowler.com/ieeeSoftware/p ... iation.pdfВсе определения ООП (старые и новые) включают в себя инкапсуляцию для обеспечения соблюдения инварианта объекта.
Нравится Yii? Давайте сделаем его лучше!.
Re: QueryBuilder и AR в отдельном пакете?
Хоть ООP, хоть OOD - основополагающая OO суть не меняется. Любое ОО - это про объекты, а не про class или extends.
В Go и Pascal класс с состоянием и поведением синтаксически описывается структурой + пачкой методов к ней. В Go наследование реализуется интерфейсами.skynin писал(а): ↑2019.08.17, 12:15 Для джунов у меня есть краткое замечание для напряжения мозга о разнице между ОО проектированием, и ОО программированием: Представть язык программирования, у которого нет ни классов, ни объектов, а только интерфейсы. (впрочем уже представлять не сложно, берем Go) И вот, для такого языка мы можем спокойно проектировать в ОО парадигме, но не можем и ОО строчки написать.
В JavaScript класс с состоянием и поведением описывается функцией с объявлением полей и методов внутри. Наследование реализуется прототипами.
А далее по ОО-строчкам разницы никакой:
Код: Выделить всё
// Go
user := User{Email: "[email protected]"}
user.Activate()
// JS
user = new User('[email protected]')
user.activate()
// PHP
user = new User('[email protected]');
user->activate();
А если думаете что классы - это про слово class и считаете что Go - это "язык программирования, у которого нет ни классов, ни объектов, а только интерфейсы" то ССЗБ.
Это всё равно, что считать, что в PHP нет переменных, так как нет слова var, а в Java нет функций, так как нет слова function.
Продвинутый джун должен вам об этом рассказать.
Штука, к которой есть поля, но нет поведения (структура или ассоциативный массив) ни в OOP, ни в OOD объектом не является.
Если ORM применять по назначению для доставания и сохранения доменных объектов, то никаких проблем нет. Если пытаться на ORM костылить листинги в UI, то опять ССЗБ.
Re: QueryBuilder и AR в отдельном пакете?
-- Любое ОО - это про объекты
только объектом можно сделать как "существительное" так и "глагол"
ООП не навязывает способ декомпозиции - как при моделировании, так и при написании кода
Кубик рубика можно описать как объект с поведением и состоянием, а можно описать клеточки и стороны и правила их взаимодействия, чтобы сэмулировать "кубик рубика"
Как удобнее - общего правила нет.
Но точно, еще раз - сами ООП не обязывают описывать "кубик рубика" как объект, ни на уровне модели, ни на уровне кода
-- В Go и Pascal, JavaScript ,
это название языков программирования.
то есть П расшифровываем как программирование.
-- А если думаете что классы, ... Это всё равно, что считать
не надо телепатировать. тем более в таком ключе - вы кретин что-ля?
Точно известно - если один считает другого кретином - то кто-то из них точно кретин. может и обое конечно
классы - это когда П - проектирование
-- Штука, к которой есть поля, но нет поведения (структура или ассоциативный массив) ни в OOP, ни в OOD объектом не является.
Конечно.
Только еще раз
объект(в ЯП) и сущность(домена) - не синонимы никак.
-- Если ORM применять по назначению для доставания и сохранения доменных объектов
а это уже П - проектирование - должен ли доменный объект сохранять свое состояние персистентно, или не должен.
В реализациях Smalltalk это происходило автоматически.
И никаких ORMов не нужно было.
В этом то и была его сила, а не только в концепции - объекты посылают друг другу сообщения.
-- то никаких проблем нет.
в игрушечных проектах, проектах показывающих концепцию, паттерны, в педагогическом коде конечно нет никаких проблем
задачу то в таких проектах ставит не действительность, а сам себе - программист
в таких проектах да, нет проблем при использовании ORM "по назначению для доставания и сохранения доменных объектов"
нет таких проблем и у архитекторов, аналитиков, которые не пишут код.
Я же видел обычно как реальные проблемы приводили в проектах к дрейфу от "просто доставания и сохранения доменных объектов" в сторону выделения сервисов, то есть выноса управления состоянием из доменных объектов.
и, как уже писал, вообще перенос управлением состоянием, в сторону хранимых процедур на сервере БД.
наоборот - как-то не припомню.
как-то не хватает фантазии представить что - выгоднее перенести управление состоянием доменных объектов из ХП или сервисов в сами эти объекты. на старте то так и было, что же потом происходит?
а потом третье П - практика.
когда оказывается что абстракции текут, компьютеры обладают конечным быстродействием, и т.д.
в теории же да, проблем нет
-- Сущность становится несамостоятельной, так как теперь не может работать полноценно в одиночку без сервиса.
это всего лишь ваши табу, и самоограничения.
не ваши лично, есть класс теоретиков которые стоят на такой позиции.
вот я об чем
причем упор то в этом утверждении на "несамостоятельность" как некий грех, дефект.
то есть - уход уже вообще в философскую плоскость, а не прагматично-инженерную
но если даже и в философскую, то там это тоже оооочень старый диспут, с контра концепциями о "взаимообусловенности", об отсутствии возможности опеределить самостоятельность.
даже у вас - если доменный объект весь такой самостоятельный - то зачем же ему ORM?
только объектом можно сделать как "существительное" так и "глагол"
ООП не навязывает способ декомпозиции - как при моделировании, так и при написании кода
Кубик рубика можно описать как объект с поведением и состоянием, а можно описать клеточки и стороны и правила их взаимодействия, чтобы сэмулировать "кубик рубика"
Как удобнее - общего правила нет.
Но точно, еще раз - сами ООП не обязывают описывать "кубик рубика" как объект, ни на уровне модели, ни на уровне кода
-- В Go и Pascal, JavaScript ,
это название языков программирования.
то есть П расшифровываем как программирование.
-- А если думаете что классы, ... Это всё равно, что считать
не надо телепатировать. тем более в таком ключе - вы кретин что-ля?
Точно известно - если один считает другого кретином - то кто-то из них точно кретин. может и обое конечно
классы - это когда П - проектирование
-- Штука, к которой есть поля, но нет поведения (структура или ассоциативный массив) ни в OOP, ни в OOD объектом не является.
Конечно.
Только еще раз
объект(в ЯП) и сущность(домена) - не синонимы никак.
-- Если ORM применять по назначению для доставания и сохранения доменных объектов
а это уже П - проектирование - должен ли доменный объект сохранять свое состояние персистентно, или не должен.
В реализациях Smalltalk это происходило автоматически.
И никаких ORMов не нужно было.
В этом то и была его сила, а не только в концепции - объекты посылают друг другу сообщения.
-- то никаких проблем нет.
в игрушечных проектах, проектах показывающих концепцию, паттерны, в педагогическом коде конечно нет никаких проблем
задачу то в таких проектах ставит не действительность, а сам себе - программист
в таких проектах да, нет проблем при использовании ORM "по назначению для доставания и сохранения доменных объектов"
нет таких проблем и у архитекторов, аналитиков, которые не пишут код.
Я же видел обычно как реальные проблемы приводили в проектах к дрейфу от "просто доставания и сохранения доменных объектов" в сторону выделения сервисов, то есть выноса управления состоянием из доменных объектов.
и, как уже писал, вообще перенос управлением состоянием, в сторону хранимых процедур на сервере БД.
наоборот - как-то не припомню.
как-то не хватает фантазии представить что - выгоднее перенести управление состоянием доменных объектов из ХП или сервисов в сами эти объекты. на старте то так и было, что же потом происходит?
а потом третье П - практика.
когда оказывается что абстракции текут, компьютеры обладают конечным быстродействием, и т.д.
в теории же да, проблем нет
-- Сущность становится несамостоятельной, так как теперь не может работать полноценно в одиночку без сервиса.
это всего лишь ваши табу, и самоограничения.
не ваши лично, есть класс теоретиков которые стоят на такой позиции.
вот я об чем
причем упор то в этом утверждении на "несамостоятельность" как некий грех, дефект.
то есть - уход уже вообще в философскую плоскость, а не прагматично-инженерную
но если даже и в философскую, то там это тоже оооочень старый диспут, с контра концепциями о "взаимообусловенности", об отсутствии возможности опеределить самостоятельность.
даже у вас - если доменный объект весь такой самостоятельный - то зачем же ему ORM?
Не желайте странного, и не будет у вас головной боли чтобы достичь этого странного.
Тем более что окажется что оно вам и не нужно было, странное это.
Тем более что окажется что оно вам и не нужно было, странное это.