Pull to refresh
2
0
Send message

Да, вы правы, спасибо.

Оператор, покупатель, это не живые организмы в реале?

Думаю неудачно написал.
Если не проектируем живых организмов, поведение думаю нет, т.е. в большинстве случаев не проектируем ничего с поведением.

Смотря где вы проведёте границу ваших абстракций.

Согласен.


А можете рассматривать как бумажку, правила валидации и соответствующий бизнес-процесс.

Но даже в этом случае вроде нет поведения, т.е. бумажка + правила о том что с ним делать.

Еще раз повторю, на всякий случай: «футуры» и «горутины» — разные вещи.

И?
Я не утверждал обратное, они решают одни и те же задачи с разными способами.
По сути футуры можно использовать как горутин, пример: выше описанная библиотека, которая в nightly, внутри использует футуры.


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

Я позицирую его как замену в будущем, поэтому специально написал про nightly, и о том что с точки зрение удобства использование у Go хороше решение.
В общем зеленых потоков есть(библиотеки), удобного интерфейса как в го нет, надеюсь так понятнее.
Пропишите +1 за удобство использование в Go, и сравните дальше.


Вот с таким подходом от прода действительно держаться нужно подальше.

Это мягко говоря не ваше дело.


Ну, как бы, сильно напоминает «ну и что, что я ковыряю в носу? Вот Паша вообще описался вчера». Ну, т.е. каким боком вообще патчи JVM относятся к обсуждению Go vs Rust?

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


Ну да, в Rust можно накрутить сборщик мусора, реализовать горутины и… получить еще один Go? Зачем?

Библиотеки уже есть, вам не нужно накрутить сборщик мусора, для мусора можете и Go использовать.


А у указанного вами фреймворка уже, видимо, и опыт применения имеется? И поведение под нагрузкой уже исследовали? А то, честно говоря, пока больше на игрушку похоже.

Интересно, откуда такие выводы?


С нагрузкой не исследовал, я вообще сантехник, и не делаю выводов исходя субъективного опыта незнакомцев.


В тех случаях, когда примитивной системы типов достаточно, она удобнее сложной. Это не очевидно?

В JS тоже достаточная система типов, по вашей логике.
Ну и как вы определяете какого уровня системы типов достаточная?


И? В чем конкретно проблема того же самого interface{}?

Насколько я понимаю, это общий интерфейс для всех типов(кроме скаляров кажетя?).
Проблема, в необходимости проверки используемых методов, и необходимость паники, когда передали неожиданный интерфейс, всё это в рантайме.
Т.е. если есть ошибка, вы его увидите только в рантайме, и только если предусмотрели это заранее.


У нас с вами разные наблюдения. Я видел старты на Go, Java, C#, Kotlin'е, видел даже на Dart'е. Кто у нас из «свежих» на расте «стартанул»?

Тогда ваше утверждение:


И есть Go, который проектировался с одной целью — бекенд. И на нем удобно пилить бэкенд. Удобнее, чем на любом из «универсальных» языков.

Неверная.


Еще раз уточню, на всякий случай:
go func() и ".and_then().and_then()" — это разные вещи. Существенно разные.

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

Да, мне тоже любопытно понять, почему anemic набирает популярность. Думаю, у её приверженцев есть достаточно сильные аргументы.

Мое мнение, anemic модели просто более есстественны, это всё же данные, например, у реального документа нет поведение.
+ это путь наименшего сопротивления, когда отсувствует четких правил по архитектуры.

Писать продакшн-код на нестабильной версии языка — моветон. Т.е. абсолютно равносильно тому, что «пока еще не завезли». При том, что в Go оно есть и работает, и не один год.

Футуры работает в стейбл, найтли может понадобитья только для сахара.
Разница в том, что этот сахар вы уже можете использовать, и мало что измениться в будущем, если вообще измениться.
А вот то что обещали, его ещё нет, совсем.
Вы же используете библиотеки, так вот пару фич из найтли получше будет чем библиотеки, люди вон патчят JVM, что гораздо страшнее, и на продакшне.


Гитхаб вам чем не угодил, собственно?

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


Вы же, надеюсь, понимаете, что никто не кинется переписывать уже работающее Go-решение на Rust`е просто потому, что туда «уже скоро завезут фичу, которая сделает его конкурентноспособным»? Найтли-фичи == еще нет в языке (с точки зрения продакшена).

  1. И без этого сахара раст более конкрунтоспособен чем Go, потому что в нем средство для программиста больше, код получается выразительнее и меньше, + проверки компилятора минимизирует ошибки.
  2. Никто не предлагает переписать Go-решение на Rust, вы внимательнее прочитайте пожалуйста, а то я питаюсь сравнивать возможностей языков, а вы про "переписать", я не говорил что Go не подходить для таких то задач, просто не называйте его более подходящим чем другие языки, это неправда.

Хм, вы уверены, что у вас есть опыт энтерпрайз-разработки? Вот, например, в энтерпрайзе очень любят SOAP, а это всегда кодогенерация, что на Java, что на C#…

А вы уверены, что прочитали мой коммент?
Речь о такого типа кодогенерацию:
https://github.com/clipperhouse/gen


В общем дело не только в кодогенерации, а в том что отсувствие дженериков, могут приводить к кодогенерацию в некоторых случаев, может приводить к дублированию, а ещё может приводить к interface{}...


Бэкенд-сервисы — это штука, которой критично важна умелка обрабатывать хренову тучу конкуррентных запросов с непрогнозируемым ростом нагрузки и падением оной. А горутины — это дешевая многопоточность. Чуете связь?

Имея нормальной многпоточности, реализовать легкие потоки возможно, есть библиотеки.
К тому же, сервису необъязательно использовать горутин на каждом шагу, в большинстве случаев достаточно использовать на уровне фреймворка.


АДТ, паттерн-матчинг и «продвинутый вывод типов» — это фичи, прилагаемые, как правило, к «развитым и гибким системам типов», т.е. для языков, разрабатываемых для подхода «от абстракции». В Go система типов примитивна, и без изменения оной перечисленные вами фичи, мягко выражаясь, малоосмыслены.

И как примитивная система типов может быть удобнее?


Не уловил, откуда взялся такой вывод…

Простое наблюдение, на бэкенде использует разных языков, и никто не собирается новых проектов начинать только на Go.


Я не уловил откуда у вас взялся обратное, пока ваши аргументы:


  • Собственно, «развесистая бизнес-логика» — отнюдь не вотчина Go. Там безраздельно царят Java и .NET, и никто их оттуда «вытеснять» и не собирается.

  • В Go система типов примитивна, и без изменения оной перечисленные вами фичи, мягко выражаясь, малоосмыслены.

  • Ну и про горутин конечно же, но почему то упорно игнорируете, всё остальное.
    Для вас, если в стабильной версии языка X, можно стартанут легкие потоки с go ..., и в языке Y с .and_then().and_then(), то автоматически можно игнорировать все удобство языка Y, логично!


Отсутствие дженериков — это, конечно, аргумент, но… а) не так больно, как вам кажется, б) используя ваши же методики аргументации — обещали «подвезти» в Go 1.6 => 2.0.

Пару фич из найтли, конкретно генераторы и новая версия макросов, это не тоже самое, что и "обещали в следующей мажорной версии".


Чем вам пакетный менеджер Go не угодил?

В этой ветке узнал о том, что ввели модули.
Это снимает част вопросов, тем неменее, например не хватает репозиторий, но это уже некритично.


Ну, как бы, за пару лет практики с необходимостью ручной генерации сталкивался пару раз. Есть, конечно, вещи вроде fasthttp или grpc, swagger и т.д. Но в fasthttp кодогенерация дает ощутимый прирост в производительности, а потому оправдана. А для grpc, swagger и прочих декларативных API-описаний, собственно, кодогенерация — это нормальный подход, везде так.

Ок, видимо избегают его.


Вы же сами сказали, что в расте (пока еще) нет полноценной замены горутинам… Т.е. пока еще удобнее, а то, что будет «потом когда-нибудь»… Хм, мне код сейчас надо писать.

Мне кажется вы видете в горутин молотока.
Меня же интерисует не только молоток, но и другие инструменты.
Вы вот промолчали про АДТ, паттерн матчинг, про компилятор с продвинутым выводом типов.
Это далеко не все, но важные вещи.




Ещё раз напомню, что мы обсуждаем (чтобы далеко не уходить от темы):


И есть Go, который проектировался с одной целью — бекенд. И на нем удобно пилить бэкенд. Удобнее, чем на любом из «универсальных» языков.

Это неверно для любого языка, особенно на бэкенде.

Связка tokio+futures-await так же удобны в использование как горутины.
Правда, для await! пока нужно использовать nightly версию языка.




А так горутины удобны, да, но это говорить лишь об одном из критерий, в котором у go хорошее решение.


Т.е. получаете горутины из коробки, но теряете например нормального менеджера пакетов, дженериков, АТД, паттерн матчинг, и помошника компилятора.


Это сложно назвать удобством, когда элементарного шаблонного кода надо генерировать...




В любой задаче, адекватно ложащейся на концепцию горутин, Go будет лучше и проще.

Голословное утверждение.
(в go может и удобно реализовать такие задачи, но не удобнее чем в ржавчине)

В общем, если хотите относительно объективных сравнений, не ставьте вопрос «чем Go лучше всех остальных языков».

Я не ставил бы этот вопрос, если бы не ваше утверждение:


И на нем удобно пилить бэкенд. Удобнее, чем на любом из «универсальных» языков.



Короче, ставьте вопрос «в каком спектре задач и почему Go лучше X(обязательно укажите интересующий вас X)».

Вопрос был почти такой же, если не придираться к словам.
Вы ответили:


Там безраздельно царят Java и .NET, и никто их оттуда «вытеснять» и не собирается.



В общем и целом, в Go нет ни одной уникальной фичи, которую нельзя бы было встретить в другом языке. Однако комбинация оных делает его предпочтительным языком для целого ряда задач. А «вот эта фича есть в языке X», и «другая фича более удобна в языке Y», и «третья фича есть в языке Z» — это мы все слышали. Только вы не будете писать проект на языке XYZ. Вам придется выбрать один.

Уникальная фича никому не нужна, нужно то что упрощает разработку и уменшает ошибки.
Хотите сравнивать конкретный юзкейс с конкретным ЯП, пожалуйста, расскажите, чем Go удобнее чем Rust, для любой задачи.
Желательно с примерами кода на обеих ЯП.

И есть Go, который проектировался с одной целью — бекенд. И на нем удобно пилить бэкенд. Удобнее, чем на любом из «универсальных» языков.

Подозреваю, что у него нет библиотек уровня hibernate/entity framework/doctrine, подобные библиотеки позволяют не писать тонну кода.


Или есть такие библиотеки? Если нет, то в чем удобство go, перед универсальными ЯП?

А при чем тут тогда двойная диспетчеризация? Кто и что диспетчеризует? То что вы описали это просто передача зависимости в виде аргумента метода.

С точки зрения получающего класса никакой разницы, кто и что там диспетчеризует.


И чем же? Тем что там все атрибуты public? Вы до конца мой комментарий дочитали? Я насчет синтаксического сахара уже сказал. В js вообще вон без этого живут

Речь не только об аттрибутах, они со статическими методами базового класса, позволяет натворить что угодно, откуда угодно.
Я ничего не говорил про синтетического сахара, просто ненадо оставлять такие вещи открытим, могуть быть неприятные побочные эффекты, я не хочу полагаться на разработчиков, когда это возможно.


А что в этом плохого?

Ничего плохого, просто вы сами не хотели сделать это на DM, через onLoad (или гидрацию).


А я и не пытаюсь скрыть все. Я пытаюсь скрыть то, что нужно для упрощения использования объекта и улучшения уровня абстракции.

Вы хотите засунуть в объект информацию о том где он хранитья.
Это повышает связанность, вместо того чтобы хранилище зависил от объекта, оба будут зависят друг от друга.


Ну так вот она и суть современных практик по использованию доктрины и DataMapper. Сделать простые структуры данных с рудиментарной логикой и обложить процедурами в виде сервисов. Об этом и говорится в первом комментарии.

То есть вы согласны, что это именно пробема использование, ок.
Я не думаю, что это проблема, тут важно исходить от проекта.
К тому же, даже если идеально реализуете AR, то он тут вам не поможет, т.к. его тоже начнуть использовать именно таким образом, пока не договоритесь внутри команды.


В общем это два противоположных похода к организации персистентности — инкапсуляция подключения к БД vs маппинг. Мне по душе больше первое, оно мне кажется ближе к ООП. Может вы думаете по-иному, предпочитаете маппинг. И в сообществе я думаю тоже будут противоположные точки зрения на этот вопрос. Я останусь при своем.

Если AR не приводить к маппингу, то я бы посмотрел на реализацию (но зачастую, в AR также существует маппинг).


Я не предпочитаю не то не другое, просто часто использую doctrine, т.к. он больше подходить для моих задач.
Я не против других подходов, тут не вопрос мнений, мне кажется, вы привели неубедительные аргументы, почему doctrine не является ООП.


Я думаю наша дискуссия затянулась, так что если хотите, можете привести свои контраргументы на этот пост и я предлагаю покончить с этим. Праздники заканчиваются).

Полностью согласен.

Речь про паттерн AR в общем, а не про какую-то конкретную реализацию.

Мы говорили про доктрину и AR, в контексте Yii, вы ранее не сообщали что речь об AR в общем.


Что понимаете под double dispatch? То что понимаю я, вообще тут не при чем.

Упращенно: Получение(внедрение) зависимостей через параметер в рантайме на основе типов.


Инкапсуляция это в первую очередь сокрытие какой-то функциональности и данных внутри объекта.

Именно, и как минимум реализация AR в Yii его нарушает.


вы предлагаете каждый раз передавать его используя внедрение метода, то есть вы скрываете меньше информации в своем объекте, чем могли бы, а это снижает уровень сокрытия информации, т.е. инкапсуляция становится более слабой.

Я предлагаю уменшить связанность, я уже написал, почему стоит внедрить зависимостей через конструктор в определенных условиях.
Программирование это компромиссы, не стоит идти в крайности и пытаться сокрыть вообще всё.
Гораздо лучше проектировать таких объектов менее связанными, стоит учитывать их жизненный цикл, который вообще то не зависит от DM/AR.


Нет, ну вы же сами написали, что сущности ближе к структурам. Вот это и имеется ввиду, когда говорят о том, что Doctrine это не ООП. А что логику поместить можно это понятно, только вот зависимости для ее работы придется всегда снаружи передавать, либо костылить с onLoad.

Что в DM что в AR(не в контексте Yii) вам всё равно придется разруливать зависимостей вручную.


Возмем простой RowDataGateway, если вы захотите внедрить зависимости к объекту реализующий RowDataGatewafInterface, то вам придеться при каждой загрузки такого объекта внедрить в него зависимостей.
Даже если будете унаследовать его от базового класса, всё равно придеться внедрить зависимости при загрузке, т.к. это будет новым объектом.
Обходный путь, сделать базовый класс со статическым состоянием, но помимо этого состояние, есть ещё проблема с другими зависимостями(как их внедрить?).


Нет, ну вы же сами написали, что сущности ближе к структурам. Вот это и имеется ввиду, когда говорят о том, что Doctrine это не ООП.

Нет, это говорить о том, что:


  • Бизнес объекты не сложные, анемичные модели не приносят проблем.
  • Либо не было договоренности об архитектуры, так часто бывает, причем это не мешает разрабатывать большие проекты, особенно когда всё на микросервисах.

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




А что вы предлагаете взамен?
Инкапсулировать всю работу с БД за интерфесом, и не отражать доменные объекты(или таблицу) в коде?

Такие вещи отдельно тестируется, и по хорошему клиентский код макает их.
Так о том и речь. Само разделение на структуры и алгоритмы (на сущности/сервисы) ничего не напоминает?

В сущности не только структура, но и логика будет.


Если вам нужна несколько объектов, вы же не зарегистрируете их как сервисы, фабрика будет более подходящим решением.
Если к этому добавить, что жизненный цикл этих объектов, такова что они должны жить пока вы их явно не удаляете, что они должны пережить перезапуск приложения, то понятное дело, внедрять туда зависимостей не лучшая идея, потому что:


  • В рамках одного типа у вас будет разные зависимости:
    <?php
    /** @var UserFactory $factory */
    $user1 = $factoroy->create(1, $someService1, $someService2);
    $user2 = $factoroy->create(2, $someService1, $someService2);
  • При восстановлении, нужно будет так же внедрить зависимостей.

Это верно не только для сущности, но и для других подобных объектов, AR тут ничем вам не помогут, кроме как глобального интерфейса, которого в 10 строк можете и на doctrine реализовать, но никому такой глобальный интерфейс на самом деле не нужно, причину надеюсь не надо объяснить.


Я не приводил никаких определений. Что странного?

Странно, что считаете нарушением инкапсуляции, то что его не нарушает, совсем:


  1. Внедрение зависимостей через double dispatch, не нарушает инкапсуляцию.
  2. Внедрение деталей хранилища нарушает принцип SRP.

Напомню, AR неплохо так нарушает как SRP, так и инкапсуляцию:


  1. Про SRP и так понятно.
  2. Публичные свойства, и методы базового класса, у которого вывернуты все кишки нарушу, это не нарушение инкапсуляции по вашему?

А где в описании паттерна говорится, что поля AR всегда должны быть публичны? Здесь реализуемо и без public.

Речь была про реализацию в Yii, или мы обсуждаем Row(Table)Gateway?

А так тут все инкапсулировано и снаружи будет только один вызов метода.
Нет, будет не так как я хотел. Будет еще код по получению объекта репозитория для передачи при вызове increaseVolume.

Репозиторий является обычным сервисом, через конструктор и получаете, поэтому именно вызвать ничего не требуется, только добавите 1 свойства к вызывающему классу.


Это решение мне тоже не по душе, оно отражает то что я уже сказал. В Doctrine вынесли подключение к БД наружу, убрали его инкапсуляцию внутри объекта. Теперь чтобы делать подобные вещи, приходится это подключение всегда снаружи передавать, пусть и обернутое в репозиторий.

По сути есть некий репозиторий, который умеет хранить данные и доставать их, вы хотите от него функционала DI, чтобы при загрузки каждого объекта он внедрил зависимости через конструктор.


Это легко реализуется, т.к. можно подключиться на событие onLoad, и вызвать конструктор с нужными параметрами, благодаря компайл тайм штукам в symfony, это будет прозрачно для пользование, и производительность не пострадает (рефлекции только в компайл тайм).


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


<?php

class User
{
    private $address;
    private $profile;

    public function __construct(Address $address, Profile $profile)
    {
        $this->address = $address;
        $this->profile = $profile;
    }
}

В Doctrine вынесли подключение к БД наружу, убрали его инкапсуляцию внутри объекта

Странное у вас определение инкапсуляции.




AR не нарушает инкапсуляцию?
Например, есть юзер, хочу поменять пароль используя метод changePassword, но не хочу чтобы пароль был доступным для чтения, как мне это реализовать в AR?
(В doctrine, я могу не создавать гет/сет методы)

В Doctrine в основу положено сознательное нарушение инкапсуляции. А это один из основополагающих принципов ООП. Вместо изоляции обращений к БД в классах AR, подключение к БД вынесено наружу. Сделано это ради того, чтобы код было легче читать и тестировать, переносить код на другие БД.

Лично мне наличие обращений к БД читать код не мешает. Перенос на другую БД? Бывает крайне редко и все равно потребуется большой рефаторинг.

С точки зрения AR, модель и есть таблица, поэтому логично, что работа с этой таблицей, инкапсулируется в нем же.


С точки зрения doctrine, модель это всего лишь доменный объект, который ничего не знает о хранилище.
Т.е. мы опираемся на объектах, находим их через репозиторий, делаем что то и сохраняем, на этом этапе не важно где храниться наши доменные объекты.
Это не значить, что мы должны абстрагироваться от конкретного БД, внутри репозиторий мы будем использовать что угодно, нет никаких ограничений, можем даже использовать AR.


Как я уже сказал в комментариях, с базой AR нормально тестится, ничего страшного там нет.

Согласен.


Код показан чисто как пример, конечно для инкремента есть более эффективное решение. Как сделать подобное в Doctrine, и чтобы код частично не протек в сервис/контроллер? А так тут все инкапсулировано и снаружи будет только один вызов метода.

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


<?php

class Product
{
    public function increaseVolume(ProductRepo $repo): void
    {
        $product = $repo->find($this->id, LockMode::PESSIMISTIC_WRITE);
        $product->volume += self::VOLUME_STEP;
        $repo->save($product);
        $repo->refresh($this);
    }
}

Заголовок спойлера
<?php

/**
 * @method Product|null find($id, $lockMode = null, $lockVersion = null)
 * @method Product|null findOneBy(array $criteria, array $orderBy = null)
 * @method Product[]    findAll()
 * @method Product[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
interface ProductRepo extends BaseRepo, ObjectRepository
{
}

class DefaultProductRepo extends ServiceEntityRepository implements ProductRepo
{
    use BaseRepoTrait;

    public function __construct(RegistryInterface $registry)
    {
        parent::__construct($registry, Product::class);
    }
}

interface BaseRepo
{
    public function save(object $product): void;
    public function refresh(object $product): void;
}

/**
 * @method EntityManager getEntityManager
 */
trait BaseRepoTrait
{
    public function save(object $entity): void
    {
        $manager = $this->getEntityManager();
        $manager->persist($entity);
        $manager->flush($entity);
    }

    public function refresh(object $entity): void
    {
        $this->getEntityManager()->refresh($entity);
    }
}
> IDE в разы лучше делает рефакторинг у статических яхыков

В строго статически типизированных языках вам и компилятор подскажет, а вот в динамике как раз без IDE сложновато.

Information

Rating
Does not participate
Registered
Activity