10 строк это замечательно, но отсутствие какого-либо исключения при обращении к несуществующему сервису не очень-то хорошо.
насколько я понял в качестве $value должна передаваться анонимная функция, котораю будет возвращать экземпляр сервиса, верно?
и спасибо за уточнение по Service Locator, действительно добавил лишню точку с запятой после private function __construct(){}
Объекты в Service Locator могут быть добавлены напрямую, через конфигурационный файл, да и вообще любым удобным программисту способом.
это применимо и к контейнерам. для того же Symfony 2 сервисы описываются в основном в конфиг файлах
но это уже имеет не столько отношение к инверсии зависимости, сколько к организации контейнера и его использованию
а что, если у Вас предусмотрена возможность выбора сервиса в администраторской панели приложения? Например, выбор payment processor. Тут уже конфигурационный файл не поможет, а зависимость в контейнере конфигурируется на основе данных в БД
но как я сказал, это уже специфика приложений, и имеет меньшее отношение к инверсии управления
Автор, спасибо за статью, с интересом прочитал.
А не было желания/идеи добавить в вашу реализацию, чего-то своего, чего не хватает в PHP, например нативной мультипоточности? Либо сделать это проблемно?
Принцип не соблюдён.
Пост-условие контракта для метода add($item) $this->data[count($this->data) - 1] == $item
В переопределённом методе данное условие не выполняется(изменено)
«Объекты в программе могут быть заменены их наследниками без изменения свойств программы»
отличается от
Пусть q(x) является свойством, верным относительно объектов x некоторого типа T. Тогда q(y) также должно быть верным для объектов y типа S, где S является подтипом типа T.
кроме языка на котором это написано?
Если я не понимаю LSP, то приведу другой пример
Удовлетворяет ли данному принципу следующий код и почему?
class DataCollector
{
protected $data = array();
public function add($item)
{
$this->data[] = $item;
}
public function getData()
{
return $this->data;
}
}
class AnotherDataCollector extends DataCollector
{
public function add($item)
{
$this->data[$item] = true;
}
}
И если мы уж говорим о ООП, то давайте не смешивать два подхода и удивляться, что что-то пошло не так, по вашему мнению
К чему я веду, да к тому что принцип подстановки Барбары Лисков не раскрыт чуть больше чем полностью. Вы говорите вообще не о том.
Если бы я создал класс, такой как пример ниже, то что-то принципиально бы изменилось?
class Calculator
{
private $width;
private $height;
public function setWidth($width)
{
$this->width = $width;
}
public function setHeight($height)
{
$this->height = $height;
}
public function calculate(Rectangle $rect)
{
$rect->setWidth($this->width);
$rect->setHeight($this->height);
return $rect->calculateSquare();
}
}
На мой взгляд, было бы чуть более интересно, если бы Вы развернули под HHVM уже готовый проект на Laravel(как примечание автора) возможно появились какие либо ошибки и т.д и заодно можно было бы кратко оценить прирост производительности
В разделе «Функции» Мартин просто в своём примере заменяет условие на функцию, никак на этом особо не заостряя внимание.
А вот Мартин Фаулер в «Рефакторинге» как раз вводит понятия «Декомпозиция условного оператора» и «Консолидация условного оператора», которые связаны с выделением условий в функции. В книге это всё описано в целом разделе «Упрощение условных выражений», в общем, есть что почитать в рамках данного вопроса
матрица принадлежности двумерная — соответственно и возникает вложенность циклов при обходе массив.
физическое применение — кластеризация объекта, например, можно с помощью него находить объекты, похожие на заданные, например какие-нибудь документы.
по поводу быстродействия
если обозначить
i — количество итераций.
n — количество кластеризируемых объектов.
c — количество кластеров
d — количество измерений(признаков), по которым кластеризируются объекты
то
временная сложность алгоритма — O(ndc^2i)
используемая память — O(nd + nc)
по поводу более наглядного примера — как только появится чуть больше свободного времени, попробую сделать что-то чуть более наглядное
И тогда причин для изменений будет практически столько же сколько действий?
Цитирую Мартина (снова):
So a responsibility is a family of functions that serves one particular actor. (Robert C. Martin)
An actor for a responsibility is the single source of change for that responsibility. (Robert C. Martin)
Вы почему-то связываете причины изменения с конкретными функциями/методами, я же Вам говорю, что причины связаны с уровнями абстракции и ответственности. Если Вы выделяете каждую функцию/метод в свой уровень абстракции/ответственности, то да, причин будет столько, сколько функций/методов. Я же так не выделяю. Я выделяю слой представления данных, слой бизнес-логики и т.д., каждый из этих общих слоев может быть разбит ещё на какие-то исходя из задач и целей проекта конкретного проекта (например, бизнес-логику зачастую удобно разбивать на абстракции на основании данных из предметной области)
А вообще функция/метод должны делать ни больше, ни меньше чем указано в их названии.
И еще вопрос, каким образом вы храните id заказа во всех классах, в виде ссылки на класс Order?
Ну, например, в случае с классом Customer я использовал композицию объекта Order. Других классов у себя я не нашёл в примере.
Встречный вопрос — какое это отношение имеет к принципам SOLID? это уже детали реализации либо я Вас неправильно понял
Уровни абстракции/ответственности, поэтому я и завёл речь об абстракциях.
Именно так, поэтому я и уточняю, раз вы говорите, что разобрались с этим вопросом, как вы определяете одну операцию?
Каждая функция/метод выполняет только те действия, которые находятся на одном уровне абстракции/ответственности и соответственно причина для её/его изменении может быть только одна
можно дойти до крайности и в другую сторону и сказать, что цикл for выполняет не одну функцию, а 2 — и инкрементирует значение, и производит сравнение :)
по сути вопроса:
лично я основываюсь на таком вот способе определения выполняет ли метод 1 функцию или нет. Он приведён в книге Мартина «Чистый код» в разделе «Правило одной операции»:
Если функция выполняет только те действия, которые находятся на одном уровне под объявленным именем, то эта функция выполняет одну операцию
Для себя я в голове делаю так: прикидываю, как без привязки к какой-либо системе, архитектуре, коду в целом должна происходить описываемая мной операция, например, получение хранилища для данных, создание объекта, заполнение его данными, сохранение, возврат результата выполнения и далее на основе этих шагов описываю её с помощью кода.
Вы верно всё поняли — вы как раз и заменил конкретную реализацию, на абстракцию и классы теперь у Вас связаны, строго говоря, слабее и Вы всегда можете добавить новый класс, реализующий IOrder, и без труда использовать его
да, всё верно, значит в первом случае Вы были правы.
это я упустил из виду, что не добавил метод или параметр для установки подтипа заказа.
вообще строго говоря такие нюансы(должны ли быть какие-то подтипы для Order и т.д.) — это детали конкретного проекта, в учебном примере я решил показать на примере класса OrderProccessor как избавиться от зависимости от конкретной реализации и перейти к зависимости от абстракции. и такой принцип может быть применён ко всем другим зависимостям, что Вы успешно и сделали в примере выше, спасибо :)
допустим метод printOrder имеет у нас следующий вид
public function printOrder($order)
{
$fields = $this->prepareOrderFields($order);
//...
return $this->printFields($fields);
}
И допустим нам захотелось поменять метод prepareOrderFields, чтобы он как-то по-другому обрабатывал поля заказа. Но окажется, что данный метод prepareOrderFields используется ещё в методе сохранения заказа и таким образом мы повлияем на сохранение заказа, сами того не ожидая
не совсем. Класс Customer не зависит от какой-то конкретной реализации класса Order, т.е. объектом currentOrder может быть и экземпляр, например, класса FreeShippingOrder, и класса LimitedItemCountOrder (классы немного надуманы, не обращайте внимания).
насколько я понял в качестве $value должна передаваться анонимная функция, котораю будет возвращать экземпляр сервиса, верно?
и спасибо за уточнение по Service Locator, действительно добавил лишню точку с запятой после private function __construct(){}
это применимо и к контейнерам. для того же Symfony 2 сервисы описываются в основном в конфиг файлах
но это уже имеет не столько отношение к инверсии зависимости, сколько к организации контейнера и его использованию
а что, если у Вас предусмотрена возможность выбора сервиса в администраторской панели приложения? Например, выбор payment processor. Тут уже конфигурационный файл не поможет, а зависимость в контейнере конфигурируется на основе данных в БД
но как я сказал, это уже специфика приложений, и имеет меньшее отношение к инверсии управления
А не было желания/идеи добавить в вашу реализацию, чего-то своего, чего не хватает в PHP, например нативной мультипоточности? Либо сделать это проблемно?
Если можно, попрошу вас привести пример нарушения принципа LSP, хотел бы разобраться
Пост-условие контракта для метода
add($item)
$this->data[count($this->data) - 1] == $item
В переопределённом методе данное условие не выполняется(изменено)
отличается от
кроме языка на котором это написано?
Если я не понимаю LSP, то приведу другой пример
Удовлетворяет ли данному принципу следующий код и почему?
Если бы я создал класс, такой как пример ниже, то что-то принципиально бы изменилось?
Не понял, что вы имеете ввиду
Мы ожидаем от программы (в данном случае метода calculateRectangleSquare) одно поведение, получаем другое. Нарушение принципа на лицо
В Вашем примере я не увидел
1) замену объекта его наследником
2) ваш объект класса TwoPointsData нигде не используется
А вот Мартин Фаулер в «Рефакторинге» как раз вводит понятия «Декомпозиция условного оператора» и «Консолидация условного оператора», которые связаны с выделением условий в функции. В книге это всё описано в целом разделе «Упрощение условных выражений», в общем, есть что почитать в рамках данного вопроса
физическое применение — кластеризация объекта, например, можно с помощью него находить объекты, похожие на заданные, например какие-нибудь документы.
по поводу быстродействия
если обозначить
i — количество итераций.
n — количество кластеризируемых объектов.
c — количество кластеров
d — количество измерений(признаков), по которым кластеризируются объекты
то
временная сложность алгоритма —
O(ndc^2i)
используемая память —
O(nd + nc)
по поводу более наглядного примера — как только появится чуть больше свободного времени, попробую сделать что-то чуть более наглядное
Цитирую Мартина (снова):
Вы почему-то связываете причины изменения с конкретными функциями/методами, я же Вам говорю, что причины связаны с уровнями абстракции и ответственности. Если Вы выделяете каждую функцию/метод в свой уровень абстракции/ответственности, то да, причин будет столько, сколько функций/методов. Я же так не выделяю. Я выделяю слой представления данных, слой бизнес-логики и т.д., каждый из этих общих слоев может быть разбит ещё на какие-то исходя из задач и целей проекта конкретного проекта (например, бизнес-логику зачастую удобно разбивать на абстракции на основании данных из предметной области)
А вообще функция/метод должны делать ни больше, ни меньше чем указано в их названии.
Ну, например, в случае с классом Customer я использовал композицию объекта Order. Других классов у себя я не нашёл в примере.
Встречный вопрос — какое это отношение имеет к принципам SOLID? это уже детали реализации либо я Вас неправильно понял
Уровни абстракции/ответственности, поэтому я и завёл речь об абстракциях.
Каждая функция/метод выполняет только те действия, которые находятся на одном уровне абстракции/ответственности и соответственно причина для её/его изменении может быть только одна
по сути вопроса:
лично я основываюсь на таком вот способе определения выполняет ли метод 1 функцию или нет. Он приведён в книге Мартина «Чистый код» в разделе «Правило одной операции»:
Для себя я в голове делаю так: прикидываю, как без привязки к какой-либо системе, архитектуре, коду в целом должна происходить описываемая мной операция, например, получение хранилища для данных, создание объекта, заполнение его данными, сохранение, возврат результата выполнения и далее на основе этих шагов описываю её с помощью кода.
это я упустил из виду, что не добавил метод или параметр для установки подтипа заказа.
вообще строго говоря такие нюансы(должны ли быть какие-то подтипы для Order и т.д.) — это детали конкретного проекта, в учебном примере я решил показать на примере класса OrderProccessor как избавиться от зависимости от конкретной реализации и перейти к зависимости от абстракции. и такой принцип может быть применён ко всем другим зависимостям, что Вы успешно и сделали в примере выше, спасибо :)
printOrder
имеет у нас следующий видИ допустим нам захотелось поменять метод
prepareOrderFields
, чтобы он как-то по-другому обрабатывал поля заказа. Но окажется, что данный методprepareOrderFields
используется ещё в методе сохранения заказа и таким образом мы повлияем на сохранение заказа, сами того не ожидая