67

Survive with OOP

Embed Size (px)

Citation preview

Page 1: Survive with OOP
Page 2: Survive with OOP

Выжить с ООП

Page 3: Survive with OOP

Макс Гопей

Page 4: Survive with OOP
Page 5: Survive with OOP

Почему?

Page 6: Survive with OOP
Page 7: Survive with OOP
Page 8: Survive with OOP

Объектно-

Oриентированное

Программирование

8

Page 9: Survive with OOP

Почему ООП?

•   Это удобно

•   Это расширяемо

•   Адекватная модель мира

•   Позволяет строить асбтракции

•   Мне так сказали

9

Page 10: Survive with OOP

Объект олицетворяет объектв реальном мире.

10

Page 11: Survive with OOP

Объект олицетворяет объектв реальном мире вашем домене.

11

Page 12: Survive with OOP

Класс — это не область имен

12

Page 13: Survive with OOP

Пожалуйста, не делайте так:

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

Page 14: Survive with OOP

Вместо:

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

Page 15: Survive with OOP

Вместо:

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

Page 16: Survive with OOP

Инкапсуляция

16

Page 17: Survive with OOP

Реализация меняется

•   разные алгоритмы

•   разные хранилища

•   разные протоколы

17

Page 18: Survive with OOP

Абстракция не меняется

18

Page 19: Survive with OOP
Page 20: Survive with OOP
Page 21: Survive with OOP

Наследование

21

Page 22: Survive with OOP
Page 23: Survive with OOP

Стакан

23

Page 24: Survive with OOP

Чашка

24

Page 25: Survive with OOP

Динозаврик с ручкой

25

Page 26: Survive with OOP
Page 27: Survive with OOP

Проектируйте для наследования.

Наследуйте, если спроектировали для этого.

27

Page 28: Survive with OOP

Полиморфизм

28

Page 29: Survive with OOP

Полиморфизм

Один интерфейс — множество реализаций.

И не важно, какая используется сейчас.

29

Page 30: Survive with OOP

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

Page 31: Survive with OOP

$cat = new Cat();

$cat->dropContainer(new Glass());

$cat->dropContainer(new Cup());

$cat->runAway();

01.

02.

03.

04.

31

Page 32: Survive with OOP

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

Page 33: Survive with OOP

Инкапсуляция помогает скрыть реализацию за абстракицей.

Наследование помогает строить абстракции.

Полиморфизм помогает писать код на основе абстракций.

33

Page 34: Survive with OOP

34

Page 35: Survive with OOP

Методы классов

35

Page 36: Survive with OOP

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

Page 37: Survive with OOP

Три вида сообщений:

•   Команда

•   Запрос

•   Документ

37

Page 38: Survive with OOP

Метод-команда

•   принимает запрос на изменение состояние объекта,

•   ничего не возвращает (void),

•   выполняется успешно, либо бросает исключение.

38

Page 39: Survive with OOP

Метод-запрос

•   принимает запрос на получение информации,

•   возвращает значение указанного типа,

•   если это невозможно, возвращает NULL ,

•   или бросает исключение,

•   никогда не меняет наблюдаемое состояние объекта.

*

*

39

Page 40: Survive with OOP

Принцип: Command Query Separation (CQS)

40

Page 41: Survive with OOP

Избегайте сеттеров

41

Page 42: Survive with OOP

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

Page 43: Survive with OOP
Page 44: Survive with OOP

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

Page 45: Survive with OOP
Page 46: Survive with OOP

Отдавайте предпочтениенеизменяемым объектам

(immutables)

46

Page 47: Survive with OOP

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

Page 48: Survive with OOP

Метод — это транзакция

48

Page 49: Survive with OOP

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

Page 50: Survive with OOP

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

Page 51: Survive with OOP

Название — это интерфейс

51

Page 52: Survive with OOP

$product = 'Ski Boots'; // product name

$product = 456; // product id

$product = [ // product data

'id' => 456,

'name' => 'Ski Boots'

];

01.

02.

03.

04.

05.

06.

52

Page 53: Survive with OOP

function getProductUrl($product) {

return '/'

. str_replace(' ', '-', strtolower($product));

}

01.

02.

03.

04.

53

Page 54: Survive with OOP

Name things by their real value

•   $product — object

•   $productName — string

•   $productId — integer / hash

•   $productData — array / structure

54

Page 55: Survive with OOP

Объекты-значения (value-objects)

55

Page 56: Survive with OOP

$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

Page 57: Survive with OOP

Объекты-значения не изменяются.

57

Page 58: Survive with OOP

Немного практики

58

Page 59: Survive with OOP

Интернет-аптека для ветеринаров.

Можно покупать товар:

•   для клиники (clinic),

•   для клиента (pet owner).

От этого зависит процесс заказа. Например, при заказе для

клиента можно оформить доставку в клинику или на дом.

59

Page 60: Survive with OOP

class Cgi_Nda_Model_Order_Mode

{

const ORDER_MODE_CLINIC = 1;

const ORDER_MODE_PET_OWNER = 2;

}

01.

02.

03.

04.

05.

60

Page 61: Survive with OOP

class Cgi_Nda_Model_Session

{

public function getOrderMode() : int {

return $this->getSessionValue('order_mode');

}

}

01.

02.

03.

04.

05.

06.

61

Page 62: Survive with OOP

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

Page 63: Survive with OOP

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

Page 64: Survive with OOP

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

Page 65: Survive with OOP

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

Page 66: Survive with OOP

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

Page 67: Survive with OOP