Pull to refresh
95
0
Андрей Нестер @andrewnester

Software Engineer @ Amazon Web Services, AWSCloud9

Send message
10 строк это замечательно, но отсутствие какого-либо исключения при обращении к несуществующему сервису не очень-то хорошо.
насколько я понял в качестве $value должна передаваться анонимная функция, котораю будет возвращать экземпляр сервиса, верно?

и спасибо за уточнение по Service Locator, действительно добавил лишню точку с запятой после private function __construct(){}
при описании сервис локатора я писал
Объекты в Service Locator могут быть добавлены напрямую, через конфигурационный файл, да и вообще любым удобным программисту способом.

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

а что, если у Вас предусмотрена возможность выбора сервиса в администраторской панели приложения? Например, выбор payment processor. Тут уже конфигурационный файл не поможет, а зависимость в контейнере конфигурируется на основе данных в БД

но как я сказал, это уже специфика приложений, и имеет меньшее отношение к инверсии управления
тут БД не главное, это всего лишь пример такой :)
Автор, спасибо за статью, с интересом прочитал.
А не было желания/идеи добавить в вашу реализацию, чего-то своего, чего не хватает в PHP, например нативной мультипоточности? Либо сделать это проблемно?
Извините, но вы не понимаете LSP

Если можно, попрошу вас привести пример нарушения принципа LSP, хотел бы разобраться
Принцип не соблюдён.
Пост-условие контракта для метода 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();
	}
}


Весь принцип сводится к интерфейсу класса.

Не понял, что вы имеете ввиду
Дело не в правильности или неправильности вычисления площади квадрата, дело в замене поведения методов класса
Принцип гласит — «Объекты в программе могут быть заменены их наследниками без изменения свойств программы»

Мы ожидаем от программы (в данном случае метода calculateRectangleSquare) одно поведение, получаем другое. Нарушение принципа на лицо

В Вашем примере я не увидел
1) замену объекта его наследником
2) ваш объект класса TwoPointsData нигде не используется
На мой взгляд, было бы чуть более интересно, если бы Вы развернули под 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 (классы немного надуманы, не обращайте внимания).

Information

Rating
Does not participate
Location
Amsterdam, Noord-Holland, Нидерланды
Date of birth
Registered
Activity