

Artem
27.06.2018
15:44:34
Есть небольшой вопрос - стоит ли делать предсказания при проверке инвариантов?
Например ящик с яблоками, у которого инвариант не больше 3х яблок.
Делается проверка в методе добавления яблок, есть 2 варианта:
1. Сначала добавить яблоки в ящик (изменить состояние), потом посчитать количество яблок и проверить на соответствие инварианту. То есть тут получается инвариант нарушается на очень короткое время и это подозрительно, но мне кажется, что в этом ничего страшного нет, т.к. всё равно дальше выкинется эксепшен.
public function addApple(Apple $apple) {
$this->apples[] = $apple;
if (count($this->apples) > 3) {
throw new Exception();
}
}
2. Посчитать количество уже имеющихся яблок, потом предсказать, что после добавления яблока их будет на 1 больше, проверить на соответствие инварианту и только потом изменить состояние.
public function addApple(Apple $apple) {
$applesCount = count($this->apples) + 1;
if ($applesCount > 3) {
throw new Exception();
}
$this->apples[] = $apple;
}


Tex
27.06.2018
15:49:48
Есть небольшой вопрос - стоит ли делать предсказания при проверке инвариантов?
Например ящик с яблоками, у которого инвариант не больше 3х яблок.
Делается проверка в методе добавления яблок, есть 2 варианта:
1. Сначала добавить яблоки в ящик (изменить состояние), потом посчитать количество яблок и проверить на соответствие инварианту. То есть тут получается инвариант нарушается на очень короткое время и это подозрительно, но мне кажется, что в этом ничего страшного нет, т.к. всё равно дальше выкинется эксепшен.
public function addApple(Apple $apple) {
$this->apples[] = $apple;
if (count($this->apples) > 3) {
throw new Exception();
}
}
2. Посчитать количество уже имеющихся яблок, потом предсказать, что после добавления яблока их будет на 1 больше, проверить на соответствие инварианту и только потом изменить состояние.
public function addApple(Apple $apple) {
$applesCount = count($this->apples) + 1;
if ($applesCount > 3) {
throw new Exception();
}
$this->apples[] = $apple;
}
делать первой строкой if($this->apples === 3) { throw new LimitApplesException(); } не?
если бы добавлялось "рандомное количество яблок", тогда да, нужен предварительный расчёт перед изменением стейта.
пёс его знает что там с этим эксепшеном сделают, вдруг просто проигнорят и пойдут дальше, а стейт уже изменился.


Vitaly
27.06.2018
15:51:23
public function addApple(Apple $apple) {
if (count($this->apples) >= self::APPLES_LIMIT - 1) {
throw new Exception();
}
$this->apples[] = $apple;
}

Artem
27.06.2018
15:51:49

Google

Artem
27.06.2018
15:53:37
3 голоса за вариант 1 и ни одного против, спасибо =)

Tex
27.06.2018
15:54:04

Vitaly
27.06.2018
15:54:16

Artem
27.06.2018
15:54:34

Vitaly
27.06.2018
15:54:47

Tex
27.06.2018
15:54:55
и @jimke в общем-то о том же.
так что не, где ты здесь нашёл "3 голоса за вариант 1" это загадка )

Artem
27.06.2018
15:55:47

Tex
27.06.2018
15:57:53
Artem давай по порядку.
1ый вариант плох, потому что ты получаешь невалидный стейт и не можешь быть уверен, что твой эксепшен не проигнорят. это не "на короткий срок", это в целом. если на вызывающей стороне будет catch (...) { // do nothing; } дальше объект будет невалидным.
2ой вариант хорош по идее, потому что решает проблему первого, но плох по реализации, зачем тебе что-то считать и складывать в отдельную переменную, если ты можешь просто чуть умнее проверить текущий стейт на возможность его изменить в нужную сторону.
хочешь добавить? убедись что есть хотя бы одно свободное место (читай count($this->apples < 3)), хочешь убрать - убедись что они вообще есть.

Vitaly
27.06.2018
16:00:49

Tex
27.06.2018
16:03:49

Sergey
27.06.2018
16:05:46

Google

Sergey
27.06.2018
16:06:16
а то что ты описал это не предсказание - это проверка предусловия и инварианта.
предсказание это когда ты не знаешь сколько яблок тебе подгонят
а если у тебя все есть и тебе буквально надо сложить 2 + 1 то это хороший вариант. И менять стэйт надо только и только тогда когда ты знаешь что все инварианты стэйта будут соблюдены

1Bot
27.06.2018
16:07:03
Пример производителей дисков увлек


Artem
27.06.2018
16:10:09
Artem давай по порядку.
1ый вариант плох, потому что ты получаешь невалидный стейт и не можешь быть уверен, что твой эксепшен не проигнорят. это не "на короткий срок", это в целом. если на вызывающей стороне будет catch (...) { // do nothing; } дальше объект будет невалидным.
2ой вариант хорош по идее, потому что решает проблему первого, но плох по реализации, зачем тебе что-то считать и складывать в отдельную переменную, если ты можешь просто чуть умнее проверить текущий стейт на возможность его изменить в нужную сторону.
хочешь добавить? убедись что есть хотя бы одно свободное место (читай count($this->apples < 3)), хочешь убрать - убедись что они вообще есть.
действительно, может такое быть с вариантом 1, про то что можно так поступить с эксепшеном не подумал =\
В общем я вроде понял идею - проверять стейт на то, находится ли он уже в пограничном положении с точки зрения инварианта
да, любопытно, это же элементарная логика на самом деле, хех


Dmitry
27.06.2018
16:35:29
new ThreeAppleCollection($apple1, $apple2, $apple3) :)

Andrew
27.06.2018
16:59:46
https://github.com/WICG/webusb о, скоро флешки по интернету можно будет заражать прям из браузера

Борис
27.06.2018
17:02:18
Скоро OS будет просто прошивкой для браузера. А затем ее вообще выпилят, как рудимент. Будут висеть объявления на столбах" установим хром недорого"

Andrew
27.06.2018
17:04:15
капец, а как php тогда запускать? срочно нужно https://github.com/oraoto/pib осваивать
а то работы лишусь

Art
28.06.2018
14:00:01
что за дичь на хабре? Велосипеды, дережабли какие-то и прочая хрень

Igor
28.06.2018
14:00:38

Art
28.06.2018
14:01:21
зачем? Нужно тонну новостей теперь скроллить

Sergey
28.06.2018
14:01:36

Art
28.06.2018
14:03:07
Это в акк входить нужно. А так еще и в вк постят в ленте

F01134H
28.06.2018
14:09:01
почему в phpunit может не работать expectException?
Делаю так:
$this->expectException(ResponseValidationException::class);
$strategy->createAddress();
по итогу все равно вываливается:
App\Node\Exceptions\ResponseValidationException: error text...

Борис
28.06.2018
14:29:28
проверь неймспейсы
если не поможет - debug + "Step Into"

Maksim
28.06.2018
14:34:09
@expectedException \App\Node\Exceptions\ResponseValidationException
а ещё так бы попробовать) стильно, модно, молодёжно)

Google

Patrik
28.06.2018
14:35:22
Нужна помощь зала, че-то я засомневался, что не полную ерунду тут сгородил)
Было несколько таблиц в БД с данными 150 сущностей (нутриентов), данные там такие, что они не поменяются никогда, например названия, меры в которых они измеряются и т.д.
Потом для каждого нутриента навалился вагон логики типа формул расчета, пришлось делать доменную модель на 150 классов описывающих особенности каждого, там же было в виде констант заведено все что было в БД (меры, названия и т.д.).
Получились как бы энтити у которых и идентификаторы есть (не суррогатные), но они неизменяемые и целиком описаны в коде...при этом в БД есть другие сущности, которые ссылаются по этим идентификаторам на эти энтити, которых в БД нет.


Sergey
28.06.2018
14:43:27
Нужна помощь зала, че-то я засомневался, что не полную ерунду тут сгородил)
Было несколько таблиц в БД с данными 150 сущностей (нутриентов), данные там такие, что они не поменяются никогда, например названия, меры в которых они измеряются и т.д.
Потом для каждого нутриента навалился вагон логики типа формул расчета, пришлось делать доменную модель на 150 классов описывающих особенности каждого, там же было в виде констант заведено все что было в БД (меры, названия и т.д.).
Получились как бы энтити у которых и идентификаторы есть (не суррогатные), но они неизменяемые и целиком описаны в коде...при этом в БД есть другие сущности, которые ссылаются по этим идентификаторам на эти энтити, которых в БД нет.
1. если все данные описаны в коде - зачем их вообще хранить в БД?


Patrik
28.06.2018
14:46:11
Ну вот я и подошел к тому чтобы грохнуть все это в бд, т.к. дублируется там все безтолково и многим повадно забирать данные оттуда.
Просто нет уверенности, что это правильный подход, получается как бы разные части агрегата персистятся кто в бд кто прямо в коде.

Roman
28.06.2018
15:07:06
Но в этом же вроде бы нет ничего плохого

Sergey
28.06.2018
15:32:48

Patrik
28.06.2018
16:33:03

Artem
28.06.2018
17:06:58
Судя по LSP получается, что инварианты могут быть ослаблены в подклассах?
Если где-то использовать A, а потом поменять на B, то тогда там где А не падало B тоже падать не будет.
class A
{
protected $x;
public function some()
{
if ($this->x > 0 && $this->x < 10) {
throw new Exception('error');
}
}
}
class B extends A
{
protected $x;
public function some()
{
if ($this->x > 0 && $this->x < 15) {
throw new Exception('error');
}
}
}

Sergey
28.06.2018
17:13:38
по LSP инваианты не могут меняться, прекондишены можно ослаблять пост кондишены можно только усиливать


Evgeniy
28.06.2018
17:31:36
тут прям абзац есть
Саттер и Александреску в своём руководстве по использованию C++ для выражения этого принципа также используют фразу «подкласс не должен требовать от вызывающего кода больше, чем базовый класс, и не должен предоставлять вызывающему коду меньше, чем базовый класс». По мнению данных авторов, публичное наследование в C++ можно употреблять только тогда, когда оно удовлетворяет принципу Лисков. Приватное наследование, по их же мнению, дозволено использовать для доступа к protected части базы и перекрытия виртуальных методов. В любом же ином случае, то есть для всего лишь повторного использования кода из базы, наследование применять нельзя.
Основания: использование публичного наследования для повторного использования кода приводит к тому, что внешний мир начинает считать класс Derived разновидностью класса Base, и возможно появление кода, явно использующего этот факт. Это сильно сужает простор для манёвра архитектора в дальнейшем поддержании и рефакторинге класса Derived.
ну и тут прям написано


Evgeniy
28.06.2018
17:32:16
Наследующий класс должен дополнять, а не изменять базовый.
даже в русской вики
https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B8_%D0%91%D0%B0%D1%80%D0%B1%D0%B0%D1%80%D1%8B_%D0%9B%D0%B8%D1%81%D0%BA%D0%BE%D0%B2
ну и слова сергея только наоборот тоже в вики про предусловия и постусловия

Artem
28.06.2018
17:45:15
как туда попал x?)
если через конструктор - тогда надо было бы проверять в конструкторе входящее значение и тогда это прекондишен и его можно ослабить =\

Sergey
28.06.2018
17:51:44

Artem
28.06.2018
17:54:08
@KuvshinovEE
«подкласс не должен требовать от вызывающего кода больше, чем базовый класс, и не должен предоставлять вызывающему коду меньше, чем базовый класс»
Это же вроде только про пре/посткондишены?

Google

Evgeniy
28.06.2018
18:00:27
у S -> Б, а T -> A в этом определение
у тебя поведение P поменяется, имхо
но это совсем строго в теории, на практике я еще ни разу не видел код который бы писали или поддерживали больше года где все бы это выполнялось

Admin
ERROR: S client not available

Artem
28.06.2018
18:06:11

Evgeniy
28.06.2018
18:09:39
ну так не только в пхп объективности ради, но в пхп особо много говна

Artem
28.06.2018
18:09:51
значит везде :D

Sergey
28.06.2018
18:12:35

Artem
28.06.2018
18:12:50

Sergey
28.06.2018
18:14:29
и да - старайся не юзать protected для стэйта. Вообще. Никогда не юзай. Просто потому что это самый простой способ нарушить LSP (а значит сломать код или начать везде расставлять instanceof)
и просто напомню - суть LSP в том что бы убедиться, что два типа могут (или не могут если принцип не соблюдается) входить в одну иерархию типов. Один контракт - могут быть объеденены абстракцией. Разные контракты - разные типы
дальше надо еще смотреть на interface segregation principle

Evgeniy
28.06.2018
18:16:46
протектед хорошо юзать в абстрактных классах, эти методы протектед абстрактными делать
но это довольно узкий пример использования)

Sergey
28.06.2018
18:17:00

Artem
28.06.2018
18:17:04
Да, вот соблюдать LSP тяжело и всякие условия с instanceof манят своей кажущейся простотой. Правда потом становится больно

Evgeniy
28.06.2018
18:17:15

Google

Sergey
28.06.2018
18:17:43

Evgeniy
28.06.2018
18:17:48
я тоже ))
но это единственный пример который я могу придумать когда protected нужно
и без него только паблик делать
и это очень очень очень редкий кейс

Антон
28.06.2018
18:19:15
а есть статейка про то на что заменить абстрактные классы? вот не первый раз слышу, но никак не могу понять до конца альтернативу

Evgeniy
28.06.2018
18:19:40
интерфейсы и трейты в пыхе
но это имхо и холиварно

Антон
28.06.2018
18:20:07
типа композицию через трейты сделать?

Evgeniy
28.06.2018
18:20:12
но если кратко, все что делалось через наследование в пыхе может быть заменено трейтом
ну не совсем композиция
это имхо могу ошибаться

Антон
28.06.2018
18:20:59
вот увидеть бы 2 примера одного кода. Через Наследование и через композицию
Фесор, может следущий доклад на эту тему?