Выжить с ООП
Макс Гопей
Почему?
Объектно-
Oриентированное
Программирование
8
Почему ООП?
• Это удобно
• Это расширяемо
• Адекватная модель мира
• Позволяет строить асбтракции
• Мне так сказали
9
Объект олицетворяет объектв реальном мире.
10
Объект олицетворяет объектв реальном мире вашем домене.
11
Класс — это не область имен
12
Пожалуйста, не делайте так:
use Spatie\Regex\Regex;
// Using `match`
Regex::match('/a/', 'abc'); // `MatchResult` object
Regex::match('/a/', 'abc')->hasMatch(); // true
Regex::match('/a/', 'abc')->result(); // 'a'
01.
02.
03.
04.
05.
06.
13
Вместо:
Regex::match('/a/', 'abc'); // `MatchResult` objectRegex::match('/a/', 'abc')->hasMatch(); // trueRegex::match('/a/', 'abc')->result(); // 'a'
лучше:
$pattern = new Regex('/a/');$matchingResult = $pattern->match('abc'); // `MatchResult` object$matchingResult->hasMatch(); // true$matchingResult->result(); // 'a'
01.02.03.
01.02.03.04.
14
Вместо:
Assertion::nullOrMax(null, 42); // successAssertion::nullOrMax(1, 42); // successAssertion::nullOrMax(1337, 42); // exception
лучше:
// since PHP 5.6Assertion\nullOrMax(null, 42); // successAssertion\nullOrMax(1, 42); // successAssertion\nullOrMax(1337, 42); // exception
01.02.03.
01.02.03.04.
15
Инкапсуляция
16
Реализация меняется
• разные алгоритмы
• разные хранилища
• разные протоколы
17
Абстракция не меняется
18
Наследование
21
Стакан
23
Чашка
24
Динозаврик с ручкой
25
Проектируйте для наследования.
Наследуйте, если спроектировали для этого.
27
Полиморфизм
28
Полиморфизм
Один интерфейс — множество реализаций.
И не важно, какая используется сейчас.
29
interface Container {
public function drop();
}
class Glass implements Container {
public function drop() { /* well, crash */ }
}
class Cup implements Container {
public function drop() { /* well, crash, and throw the handle out */ }
}
class Cat {
public function dropContainer(Container $container) {
$container->drop();
}
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
30
$cat = new Cat();
$cat->dropContainer(new Glass());
$cat->dropContainer(new Cup());
$cat->runAway();
01.
02.
03.
04.
31
class ContainerCollection implements Iterator { public function current() : Container { /* ... */ }; // ...} $containersOnTable = new ContainerCollection(); // Your mom fills the collection here:$eventManager->dispatch('serve_table', $containers); array_walk($containersOnTable, function(Container $container) use ($cat) { $cat->dropContainer($container); });
01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.
32
Инкапсуляция помогает скрыть реализацию за абстракицей.
Наследование помогает строить абстракции.
Полиморфизм помогает писать код на основе абстракций.
33
34
Методы классов
35
class SearchEngine {
public function indexProduct(Product $product) {
$addToIndex = $product->isVisible();
if ($addToIndex) {
$this->productIndex->add($product);
}
}
}
01.
02.
03.
04.
05.
06.
07.
08.
36
Три вида сообщений:
• Команда
• Запрос
• Документ
37
Метод-команда
• принимает запрос на изменение состояние объекта,
• ничего не возвращает (void),
• выполняется успешно, либо бросает исключение.
38
Метод-запрос
• принимает запрос на получение информации,
• возвращает значение указанного типа,
• если это невозможно, возвращает NULL ,
• или бросает исключение,
• никогда не меняет наблюдаемое состояние объекта.
*
*
39
Принцип: Command Query Separation (CQS)
40
Избегайте сеттеров
41
class Person { private $firstName, $lastName, $gender, $email; // __constructor() // getters // setters} $person = new Person('Sheldon', 'Cooper', 'M', '[email protected]');render($person);
01.02.03.04.05.06.07.08.09.
42
class Person { private $firstName, $lastName, $gender, $email; // __constructor() // getters // setters} $person = new Person('Sheldon', 'Cooper', 'M', '[email protected]'); $person->setFirstName('Penny');$person->setGender('F'); render($person);
01.02.03.04.05.06.07.08.09.10.11.12.13.
44
Отдавайте предпочтениенеизменяемым объектам
(immutables)
46
class Person { private $firstName, $lastName, $email, $gender; public function rename(NameChangingRequest $request) { // change first/last/... names depending on request // throw exception if name is not male, for instance } public function changeGender(GenderChangingRequest $request) { // A request which contains also the new name, // maybe the reason or whatever is needed. }} $person->changeGender(new GenderChangingRequest('M', 'New Name'));
01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.
47
Метод — это транзакция
48
class Product { public function reduceQuantity($deltaQuantity) { $this->quantity -= $deltaQuantity; } public function verifyStockAvailability() { if ($this->quantity == 0) { $this->removeFromStock(); } }} $product->reduceQuantity($orderedQuantity);$product->verifyStockAvailability();
01.02.03.04.05.06.07.08.09.10.11.12.13.
49
class Product { private function reduceQuantity($deltaQuantity) { /*...*/ } private function verifyStockAvailability() { /*...*/ } public function takeFromStock($quantity) { try { $this->reduceQuantity($orderedQuantity); $this->verifyStockAvailability(); } catch() { // ... } }} $product->takeFromStock($orderedQuantity);
01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.
50
Название — это интерфейс
51
$product = 'Ski Boots'; // product name
$product = 456; // product id
$product = [ // product data
'id' => 456,
'name' => 'Ski Boots'
];
01.
02.
03.
04.
05.
06.
52
function getProductUrl($product) {
return '/'
. str_replace(' ', '-', strtolower($product));
}
01.
02.
03.
04.
53
Name things by their real value
• $product — object
• $productName — string
• $productId — integer / hash
• $productData — array / structure
54
Объекты-значения (value-objects)
55
$person->addContactInformation(
new EmailAddress('[email protected]')
);
$person->addContactInformation(
new LinkedInProfileUrl('@max.gopey')
);
$this->redirect(new Url('https://stackoverflow.com'));
01.
02.
03.
04.
05.
06.
07.
56
Объекты-значения не изменяются.
57
Немного практики
58
Интернет-аптека для ветеринаров.
Можно покупать товар:
• для клиники (clinic),
• для клиента (pet owner).
От этого зависит процесс заказа. Например, при заказе для
клиента можно оформить доставку в клинику или на дом.
59
class Cgi_Nda_Model_Order_Mode
{
const ORDER_MODE_CLINIC = 1;
const ORDER_MODE_PET_OWNER = 2;
}
01.
02.
03.
04.
05.
60
class Cgi_Nda_Model_Session
{
public function getOrderMode() : int {
return $this->getSessionValue('order_mode');
}
}
01.
02.
03.
04.
05.
06.
61
class Cgi_Nda_Block_Order_Mode_Info{ public function getOrderMode () { $orderMode = $this->_getSession()->getOrderMode(); if ($orderMode) { if ($orderMode == Cgi_Nda_Model_Order_Mode::ORDER_MODE_PET_OWNER) { return 'For pet owner' ; } elseif ($orderMode == Cgi_Nda_Model_Order_Mode::ORDER_MODE_CLINIC) { return 'For clinic' ; } else { return false; } } else { return false; } } public function isSeparateShippingAddressAllowed () { $orderMode = $this->_getSession()->getOrderMode(); return $orderMode && $orderMode == Cgi_Nda_Model_Order_Mode::ORDER_MODE_PET_OWNER }}
01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.
62
interface Cgi_Nda_Model_Order_Mode_Interface{ public function getCode() : int; public function getTitle() : string; public function isSeparateShippingAddressAllowed() : bool;}
01.02.03.04.05.06.07.08.
63
class Cgi_Nda_Model_Order_Mode_Clinic implements Cgi_Nda_Model_Order_Mode_Interface{ public function getCode() : int { return 1; } public function getTitle() : string { return 'For Clinic'; } public function isSeparateShippingAddressAllowed() : bool { return false; }} class Cgi_Nda_Model_Order_Mode_PetOwner implements Cgi_Nda_Model_Order_Mode_Interface{ public function getCode() : int { return 2; } public function getTitle() : string { return 'For Pet Owner'; } public function isSeparateShippingAddressAllowed() : bool { return true; }}
01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.
64
class Cgi_Nda_Model_Session
{
public function getOrderMode()
: Cgi_Nda_Model_Order_Mode_Interface {
return $this->getSessionValue('order_mode');
}
}
01.
02.
03.
04.
05.
06.
07.
65
class Cgi_Nda_Block_Order_Mode_Info{ private $orderMode; public function __construct( Cgi_Nda_Model_Order_Mode_Interface $orderMode ) { $this->orderMode = $orderMode; } public function getOrderModeTitle() { return $this->orderMode->getTitle(); } public function isSeparateShippingAddressAllowed() { return $this->orderMode->isSeparateShippingAddressAllowed(); }}
01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.
66
Top Related