Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
public function removeItem($item){
if(is_null($this->currentOrder)){
return false;
}
return $this->currentOrder = new Order();
}
$this->config->getDns(), а$this->config->getDsn()… создать интерфейc IOrderSource, который будет реализовывать соответствующими класса MySQLOrderSource, ApiOrderSource и так далее...Кажется я не понял. У нас интерфейс с реализацией?
… создать интерфейc IOrderSource, который будет реализовывать соответствующими класса MySQLOrderSource, ApiOrderSource и так далее...
… создать интерфейc IOrderSource, который будет реализовываться соответствующими классами MySQLOrderSource, ApiOrderSource и так далее...
Все проблемы программирования можно решить дополнительным слоем абстракции… кроме проблемы избыточной абстракции
Проверяем, зависят ли классы от каких-то других классов(непосредственно инстанцируют объекты других классов и т.д) и если эта зависимость имеет место, заменяем на зависимость от абстракции.
В случае с несоблюдением LSP мы получаем изменчивость в поведении там, где мы её не ждали.
sub processOperation
{
if ($operation->type eq 'refund') {
$operation->amount = $operation->amount * -1;
}
…
$operation->process();
}Пусть q(x) является свойством, верным относительно объектов x некоторого типа T. Тогда q(y) также должно быть верным для объектов y типа S, где S является подтипом типа T
function myRectangle(Rectangle $rectangle)
{
$rectangle->setWidth(4);
$rectangle->setHeight(5);
return $rectangle->getHeight * $rectangle->getWidth == 20;
}
myRectangle(new Rectangle()) // true;
myRectangle(new Square()) // false;
Пример с функцией показывает, что могут существовать функции, которые работают с указателями или ссылками на объекты Rectangle, но не могут оперировать с объектами Square. Таким образом, при использовании таких функций объект Square не является заменяемым для Rectangle, взаимосвязь между такими объектами нарушает принцип подстановки Лискоу
function calculateRectangleSquare(Rectangle $rectangle, $width, $height)
{
if (get_class($rectangle) == "Square") {
$rectangle->setWidth($width);
} else {
$rectangle->setWidth($width);
$rectangle->setHeight($height);
}
return $rectangle->getHeight * $rectangle->getWidth;
}
// точка входа - index.php
//...
$config = new Config('path/to/conf');
$app = new Application($config);
$app->run();
// example controller
class ExampleController extends BaseController
{
//...
public function saveAction()
{
$config = $this->getApplication()->getConfig();
$respository = $config->getServices()->getRepositoryManager()->getRepository('Order');
//...
}
}
// если хотим изменить источник, можно написать что-нибудь такое
public function saveAction()
{
$config = $this->getApplication()->getConfig();
$respository = $config->getServices()->getRepositoryManager()->getRepository('Order');
$repository->setSource(new ApiOrderSource($config->getSources()->getApi()));
//...
}
// сам файл конфигурации может выглядеть как-нибудь так
//...
{
'services' : {
'RepositoryManager' : {
'class' => 'RepositoryManager',
'options' => {
//...
}
},
//...
},
'sources' : {
'MySQL' : {
'host' => 'host'
'username' => 'name',
'password' => 'password',
'dbname' => 'dbname'
},
'API' : {
'getaway' => 'http://apipath.com/api',
'oauth_key' => '',
}
}
}
//...
Если функция выполняет только те действия, которые находятся на одном уровне под объявленным именем, то эта функция выполняет одну операцию
Для себя я в голове делаю так: прикидываю, как без привязки к какой-либо системе, архитектуре, коду в целом должна происходить описываемая мной операция, например, получение хранилища для данных, создание объекта, заполнение его данными, сохранение, возврат результата выполнения и далее на основе этих шагов описываю её с помощью кода.
можно дойти до крайности и в другую сторону и сказать, что цикл for выполняет не одну функцию, а 2 — и инкрементирует значение, и производит сравнение :)
Если функция выполняет только те действия, которые находятся на одном уровне под объявленным именем, то эта функция выполняет одну операцию
Что за уровни?
Это речь об абстракции, а не о единой операции.
Именно так, поэтому я и уточняю, раз вы говорите, что разобрались с этим вопросом, как вы определяете одну операцию?
Каждая функция/метод выполняет только те действия, которые находятся на одном уровне абстракции/ответственности и соответственно причина для её/его изменении может быть только одна
И тогда причин для изменений будет практически столько же сколько действий?
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?
На более простых словах это можно описать так — все классы, функции и т.д. должны проектироваться так, чтобы для изменения их поведения, нам не нужно было изменять их исходный код.
1. They are “Open For Extension”.
This means that the behavior of the module can be extended. That we can make
the module behave in new and different ways as the requirements of the application
change, or to meet the needs of new applications.
2. They are “Closed for Modification”.
The source code of such a module is inviolate. No one is allowed to make source
code changes to it.
printOrder имеет у нас следующий видpublic function printOrder($order)
{
$fields = $this->prepareOrderFields($order);
//...
return $this->printFields($fields);
}
prepareOrderFields, чтобы он как-то по-другому обрабатывал поля заказа. Но окажется, что данный метод prepareOrderFields используется ещё в методе сохранения заказа и таким образом мы повлияем на сохранение заказа, сами того не ожидаяpublic function addItem($item){
if(is_null($this->currentOrder)){
$this->currentOrder = new Order();
}
return $this->currentOrder->addItem($item);
}
//...
public function addItem(IOrder $currentOrder, $item){
return $currentOrder->addItem($item);
}
//...
//...
$oOrder = $oCustomer->currentOrder ?: new Order();
$oCustomer->addItem($oOrder, $oItem);
calculateRectangleSquare(new Square, 4, 5); // 25 ???
class Rectangle
{
protected $width;
protected $height;
public function __construct($width, $height ) {
$this->width = $width;
$this->height = $height;
}
public function getWidth()
{
return $this->width;
}
public function getHeight()
{
return $this->height;
}
public function calculateSquare()
{
return $this->height * $this->width;
}
}
class Square extends Rectangle
{
public function __construct($size) {
parent::__construct($size, $size);
}
}
class TwoPointsData
{
private $width;
private $height;
public function getWidth()
{
return $this->width;
}
public function setWidth($width)
{
$this->width = $width;
}
public function getHeight()
{
return $this->height;
}
public function setHeight($height)
{
$this->height = $height;
}
}
class Rectangle
{
public function __construct(TwoPointsData $data)
{
...
}
public function calculateSquare()
{
return $this->height * $this->width;
}
}
class Square extends Rectangle
{
public function __construct(TwoPointsData $data)
{
...
}
}
Принцип гласит — «Объекты в программе могут быть заменены их наследниками без изменения свойств программы»
Дело не в правильности или неправильности вычисления площади квадрата, дело в замене поведения методов класса
Принцип гласит — «Объекты в программе могут быть заменены их наследниками без изменения свойств программы»
Пусть q(x) является свойством, верным относительно объектов x некоторого типа T. Тогда q(y) также должно быть верным для объектов y типа S, где S является подтипом типа T.
«Объекты в программе могут быть заменены их наследниками без изменения свойств программы»
Пусть q(x) является свойством, верным относительно объектов x некоторого типа T. Тогда q(y) также должно быть верным для объектов y типа S, где S является подтипом типа T.
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;
}
}
add($item)$this->data[count($this->data) - 1] == $itemИзвините, но вы не понимаете LSP
class A
{
protected $data;
public function add($item)
{
$this->data[] = $item;
}
}
class B extends A
{
public function add($item)
{
if ($item < 52) {
throw new Exception("Итем не может быть меньше 52");
}
$this->data[] = $item;
}
}
И если мы уж говорим о ООП, то давайте не смешивать два подхода и удивляться, что что-то пошло не так, по вашему мнению
К чему я веду, да к тому что принцип подстановки Барбары Лисков не раскрыт чуть больше чем полностью. Вы говорите вообще не о том.
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();
}
}
Весь принцип сводится к интерфейсу класса.
Подскажи, пожалуйста, у тебя есть такой кусок в разделе "Принцип открытости/закрытости (Open-closed)":
public function setSource(IOrderSource $source)
{
$this->source = $source;
}Тут мы для получаемого объекта $source устанавливаем тип интерфейса IOrderSource.
В $source дожен быть объект, который наследует этот интерфейс, у меня так:
class ServiceEmail implements IOrderSource {}Но если я потом устанавливаю его так
$message->setService(new ServiceEmail());То получаю ошибку
Uncaught TypeError: Argument 1 passed to
Service\Notification::setService() must be an instance of
Service\IOrderSource,
instance of
Service\ServiceSMS
givenТо есть, ему не нравится то, что тип не совпадает с указанным
Правильно ли указывать в подсказке типа интерфейс? Или я что-то не так делаю?
Шпаргалка по SOLID-принципам с примерами на PHP