Lionishy
Новичок
Хрестоматийный пример Пирса: обработка ошибок, когда функция может выполнить работу и вернуть объект, а может вернуть ошибку, совершенно другой тип.Если же это совсем разные типы, то у них не может быть одного обработчика.
Хрестоматийный пример Пирса: обработка ошибок, когда функция может выполнить работу и вернуть объект, а может вернуть ошибку, совершенно другой тип.Если же это совсем разные типы, то у них не может быть одного обработчика.
Этого делать не следует не в ООП, а в именованной системе типов. Вот в Java так делать, действительно, категорически нельзя, потому что нет механизма контроля случайных ошибок, нет защиты от дурака для таких конструкций. Но Java -- это очень убогий ООП, на уровне древних C++, Oberon и Simula. Возможно, даже одна из худших ООП реализаций (даже хуже С++), это тема отдельной дискуссии.А в ООП так делать не надо никогда
Исключения не могут заменить частично определённых функций. И это тоже отдельная тема для дискуссии.есть исключения для этого.
Средняя программа на любом широко используемом языке выглядит как говно. Для flow control в ООП есть известные паттерны, они работают, а то, что они многословны - я недостатком не считаю.Я смотрел, как выглядят средние PHP коды
Предлагаю считать каноничным императивным ООП вообще untyped. Luca Cardelliканоничным ООП Smalltalk
Я и не спорю. Скала тому пример. А если ширше смотреть, можно и процессы в Erlang можно вполне себе считать объектами, вполне по smalltalk-овски.Между ООП и ФП нет границы, это нечто взаимно перпендикулярное (или близкое к тому)
К концепции объектов у меня претензий, конечно, нет.Вот только непонятны твои претензии к ООП.
С валидацией не всё очевидно... Если это предметная валидация (строка не длиннее 30 символов, число строго больше нуля и т.д.), то может быть лучше определить типы с нужными ограничениями и присваивать эти метки внутренним полям ValueObject?В value object может быть валидация, всякие там assert-ы
Тут всё очень запутанно. В принципе ООП легко может справиться со стрелочными объектами.first-class functions
interface Nat {
public function succ();
public function pred();
public function is_zero();
}
class NonZero implements Nat {
public function succ()
{
return new NonZero(this);
}
public function pred()
{
return $this->pred;
}
public function is_zero()
{
return false;
}
public function __construct($pred)
{
$this->pred = $pred;
}
private $pred;
}
class Zero implements Nat {
public function succ()
{
return new NonZero(this);
}
public function pred()
{
throw new Exception("Operational semantic violation");
}
public function is_zero()
{
return true;
}
}
class Summ implements Nat {
public function succ()
{
return NonZero(
this->summ());
}
public function pred()
{
return this->summ()->pred();
}
public function is_zero()
{
return this->summ()->is_zero();
}
/**
* @param I Nat
* @param I Nat
*/
public function __construct($lha,$rha)
{
$this->lha = $lha; $this->rha = $rha;
}
private function summ($a,$b)
{
if ($a->is_zero())
return $b;
return $this->summ(
$a->pred(),
new NonZero($b));
}
private $lha;
private $rha;
}
interface F {
f(A: a) -> B: b;
}
class F implements F {
f(A: a) -> B: b
b = f(a);
}
Да -- это ужасно!я могу безболезненно сложить два инта weight и length
Эти типы и будут value object'ами. И да, что значит «предметная валидация»? Ты так называешь поддержание внутренних инвариантов объекта?С валидацией не всё очевидно... Если это предметная валидация (строка не длиннее 30 символов, число строго больше нуля и т.д.), то может быть лучше определить типы с нужными ограничениями и присваивать эти метки внутренним полям ValueObject?
Всё наоборот. Доменная валидация VO должна быть в самом VO. Но ты приводишь в качестве примера «переданный e-mail не содержится в базе адресов», то время как этот инвариант просто не является внутренним для VO Email, поэтому проверять это внутри него абсурдно. Собственно, я не улавливаю разницы с твоей «предметной валидацией», есть только семантическое различие. Это те же самые проверки инвариантов на уровне «строго больше нуля», просто это уже бизнес-инварианты, поскольку речь идет про модель.Если это доменная валидация данных (данные обязательно введены пользователем, переданный e-mail не содержится в базе адресов и т.д.), она, как мне кажется, обязательно должна быть отделена от ValueObject.
Эти типы и будут value object'ами.
"Поддержание" -- это неверное слово. Объект просто не может быть создан с нарушением инварианта.поддержание внутренних инвариантов объекта?
Доменная валидация -- это управление потоком. То есть, нет нарушения операционной семантики, поступление неверных данных -- штатная ситуация. Например, если пользователь ввёл неверные данные -- переспроси. То есть, этим занимается модель процесса, а не модель данных.Доменная валидация VO должна быть в самом VO
Что такое «стрелочный объект»?Нет. Это будут стрелочные объекты
Что ты имеешь в виду? Зачем тебе иметь VO, который не поддерживает свои собственные инварианты?, которые преобразуют объект ValueObject в ValidValueObject
У тебя есть более удачный термин?"Поддержание" -- это неверное слово. Объект просто не может быть создан с нарушением инварианта.
Капитан очевидность говорит, что доменная валидация — это проверки domain-инвариантов. На уровне модели эти проверки должны быть жесткими и падать с исключением. Примерно то же самое ты подразумеваешь под нарушением операционной семантики, насколько я понимаю.Доменная валидация -- это управление потоком.
namespace Blizzard\Hearthstone\GameDomain;
final class AvailableMana
{
public function __construct(int $mana)
{
Assertion::range($mana, 0, 10);
$this->mana = $mana;
}
// ...
}
Я не понимаю какое отношение ввод имеет к домену. Это имеет смысл, если мы моделируем, скажем, клавиатуру. В остальных случаях это представление. То, о чем ты сейчас говоришь, называется клиентской валидацией и может быть на стороне клиента (iOS-приложение, JavaScript, etc.), само по себе это исключает того факта, что модель должна поддерживать свою внутреннюю непротиворечивость.Например, если пользователь ввёл неверные данные -- переспроси. То есть, этим занимается модель процесса, а не модель данных.
Принято говорить, что объект стрелочный, если этот объект есть функция.Что такое «стрелочный объект»?
Тут я что-то невразумительно написал. Комментарием попытался исправить, всё равно не то. Это не надо читать.Что ты имеешь в виду?
namespace Blizzard\Hearthstone\GameDomain;
final class AvailableMana
{
public function __construct(int $mana)
{
Assertion::range($mana, 0, 10);
$this->mana = $mana;
}
// ...
}
namespace Blizzard\Hearthstone\GameDomain;
final class AvailableMana
{
public function __construct(IntRange<0,10> $mana) //если бы PHP были типы, параметризуемые значениями
{
$this->mana = $mana;
}
// ...
}
Т.е. эта конструкция у тебя молчаливо принимает какое-то из значений, если туда передать 11?А IntRange мог бы иметь рекурсивный конструктор, который как ни крути, ничего кроме 0 .. 10 выдать не может.
Если нет правила редукции для AvailableMana, когда туда попало число 11, то этот вопрос можно и нужно решить на уровне типа. И это не просто "сахарок". Это строгое разделение ответственности, которое позволяет разбить формальное доказательство корректности на аккуратные леммы. Я уже не буду лазить в AvailableMana и проверять, а не получилось ли так, что там int принял какое-то значение неверное. Я точно знаю, что этого быть не может -- у меня есть лемма IntRange<0,10>.Я не понимаю
Можно создать, конечно, и сколь угодно сложный тип.Я вижу обычный сахар для частного случая. Там могут быть и более сложные проверки инвариантов, включающие регулярные выражения и прочее.
Ты всего-навсего предлагаешь декомопозировать один VO на несколько более мелких VO без какой-либо разумной аргументации. Я точно знаю, что AvailableMana не может быть вне 0..10, потому что я вижу эту строку:Я уже не буду лазить в AvailableMana и проверять, а не получилось ли так, что там int принял какое-то значение неверное. Я точно знаю, что этого быть не может -- у меня есть лемма IntRange<0,10>.
<...>
Как устроен IntRange<0,10> AvailableMana плевала с высокой башни.
<...>
Можно создать, конечно, и сколь угодно сложный тип.
Assertion::range($mana, 0, 10)