Вы находитесь на странице: 1из 48

Архитектура, алгоритмы и паттерны на PHP

Урок 2
Принципы
проектирования
UML
На уроке разберём

● Понятие шаблонов (паттернов) проектирования.


● Кто их придумал и откуда они взялись?
● Зачем они нужны и как они помогают в разработке?
● Что такое язык UML и как он может использоваться в
проектировании ПО?
Язык UML

UML (Unified Modeling Language, унифицированный язык


моделирования)

Последняя версия UML 2.5 опубликована в июне 2015 года.

UML 2.4.1 принят в качестве международного стандарта.


Модель UML — это совокупность
диаграмм
Диаграммы UML 2.x
Диаграмма классов
Видимость атрибутов
Вид Название

+ Публичный (Public)

- Приватный (Private)

# Защищённый (Protected)

/ Производный (Derived) (может быть совмещён с


другими)

~ Пакет (Package)
Виды отношений
Название Графический вид Описание

Зависимость Изменение спецификации класса-поставщика может повлиять на работу зависимого


класса, но не наоборот.

Ассоциация Показывает, что можно перемещаться от объектов одного класса к другому.

Агрегация Разновидность ассоциации при отношении между целым и его частями. Встречается,
когда один класс является коллекцией или контейнером других.

Композиция Более строгий вариант агрегации. Имеет жёсткую зависимость времени существования
экземпляров класса контейнера и экземпляров содержащихся классов. Если контейнер
будет уничтожен, то всё его содержимое будет также уничтожено.

Обобщение / Показывает, что один из двух связанных классов (подтип) является частной формой
наследование другого (надтипа), который называется обобщением первого.

Реализация Один элемент (клиент) реализует поведение, заданное другим (поставщиком).


Кратность отношений

0 .. 1 Ноль или один объект

1 Строго один объект

* Ноль или более объектов

1 .. * Один объект и более


<?php <?php

Ассоциация class Person


{
class Card
{
protected $name; private $number;
protected Card $card;
public function setNumber($number)
public function getName() {
{ $this->number = $number;
return $this->name; }
} }

public function setName($name)


{
$this->name = $name;
}

public function getCard()


{
return $this->card;
}

public function setCard($card)


{
$this->card = $card;
}
}
Агрегация
Обобщение
<?php

class Person
{
protected $name;

public function getName()


{
return $this->name;
}

public function setName($name)


{
$this->name = $name;
}
}

class Developer extends Person


{
private $position;
public $salary;

public function getPosition(){


return $this->position;
}

}
Зависимость
<?php

class Menu
{
public $nameMenu = [];

public function run(){


for ($i=0; $i < 10; $i++){
$item = new ItemMenu();
$data = $item->getItem();
}
}
}

class ItemMenu
{
public $href;
public $title;

public function getItem(){


return $this->href . ' - '.$this->title;
}
}
Реализация
<?php

interface Transport
{
public function drive();
public function beep();
}

class KickScooter implements Transport


{
public StreetWheel $streetWheel;
public Beep $beep;

public function drive() {}

public function beep() {}

}
Диаграмма последовательностей
Разработчики постоянно решают
одни и те же задачи проектирования
Для многих проектировочных
задач уже найдено хорошее
решение, которое можно
применить!
Шаблон ≠ готовый код

Шаблон = абстрактное решение с


общепринятым названием
Классификация шаблонов

● Единой и общепризнанной нет, так как


шаблонов много на разных уровнях
абстракции.

● Для ООП — в книге «банды четырёх» —


Design Patterns: Elements of Reusable
Object-Oriented Software.
Шаблоны GoF
Принципы образования шаблонов

● Общепринятые для разработки ПО: DRY,


KISS.
● Для ООП: SOLID.
● Обобщённые для ООАП: GRASP.
Принцип DRY

Don’t repeat yourself

«Не повторяйся»
Keep it super simple

«Не усложняй»
Принципы SOLID
1. Принцип единственной ответственности
(The Single Responsibility Principle)
2. Принцип открытости / закрытости
(The Open Closed Principle)
3. Принцип подстановки Барбары Лисков
(The Liskov Substitution Principle)
4. Принцип разделения интерфейса
(The Interface Segregation Principle)
5. Принцип инверсии зависимостей
(The Dependency Inversion Principle)
Принцип единственной ответственности

“ Существует лишь одна причина,


приводящая к изменению класса


Принцип единственной ответственности

class Basket
{
class Basket public function getItems() {/**/}
{ public function getTotalCount() {/**/}
public function getItems() {/**/} public function getTotalPrice() {/**/}
public function
public function
public function
getTotalCount() {/**/}
getTotalPrice() {/**/}
save() {/**/}
→ }

class BasketRepository
public function update() {/**/} {
public function clear() {/**/} public function save() {/**/}
} public function update() {/**/}
public function clear() {/**/}
}
Принцип открытости / закрытости

“ Программные сущности (классы, модули,


функции и т. п.) должны быть открыты
для расширения, но закрыты для
изменения

Принцип открытости / закрытости
class Basket
{
public function getTotalPrice(): float
{
$total = 0;
foreach ($this->items as $item) {
$total += $item->getPrice();
}

if ($this->promoCode->isActivatedByUser($this->getUser()) === true) {


$discount = $this->promoCode->getDiscount($this->getUser());
$total = $total * (1 - $discount / 100.00);
}

return $total;
}
}
Принцип открытости / закрытости
abstract class Basket
{
public function getTotalPrice()
{
$products = $this->getProducts();
$discount = $this->getDiscount();

return $this->calculate($products, $discount);


}

abstract public function getProducts();


abstract public function getDiscount();

public function calculate(array $products, array $discount)


{
echo 'Рассчитываем итоговую сумму заказа';
}
}
Принцип подстановки Барбары Лисков

“ Объекты в программе могут быть


заменены их наследниками без изменения
свойств программы


Принцип подстановки Барбары Лисков
class Rectangle
{
class Square extends Rectangle
protected $width;
{
protected $height;
public function setWidth(int $width): void
{
public function setWidth(int $width): void
parent::setWidth($width);
{ $this->width = $width; }
parent::setHeight($width);
Прямоугольник } Квадрат
public function setHeight(int $height): void
{ $this->height = $height; } public function setHeight(int $height): void
{
public function getWidth(): int parent::setHeight($height);
{ return $this->width; } parent::setWidth($height);
}
public function getHeight(): int }
{ return $this->height; }
}
Принцип подстановки Барбары Лисков
public function getSquare(Rectangle $rectangle, int $width, int $height): int
{
$rectangle->setWidth($width);
$rectangle->setHeight($height);

return $rectangle->getHeight() * $rectangle->getWidth();


}

getSquare(new Rectangle(), 4, 5); // 20


getSquare(new Square(), 4, 5); // 25
Принцип подстановки Барбары Лисков
class Rectangle
{
class Square
protected $width;
{
protected $height;
protected $size;

public function setWidth(int $width): void


public function setSize(int $size): void
{ $this->width = $width; }
{
$this->size = $size;
public function setHeight(int $height): void
}
{ $this->height = $height; }

public function getSize(): int


public function getWidth(): int
{
{ return $this->width; }
return $this->size;
}
public function getHeight(): int
}
{ return $this->height; }
}
Принцип разделения интерфейсов

“ Множество специализированных
интерфейсов лучше, чем один
универсальный


Принцип разделения интерфейсов
/* Общий интерфейс для товаров */
interface IItem
{
public function setCondition();
public function setPrice();
}
interface IItem
{ /* Интерфейс для одежды */
public function setSize();


interface IWearable
public function setCondition(); {
public function setMaterial(); public function setMaterial();
public function setBestBeforeDate(); public function setSize();
public function setPrice(); }
}
/* Интерфейс для портящихся товаров */
interface IDegradable
{
public function setBestBeforeDate();
}
Принцип разделения интерфейсов
/* Рубашка */
class Shirt implements IItem, IWearable
{
public function setSize() {/**/}
public function setCondition() {/**/}
public function setMaterial() {/**/}
public function setPrice() {/**/}
}

/* Томатный суп */
class TomatoSoup implements IItem, IDegradable
{
public function setCondition() {/**/}
public function setPrice() {/**/}
public function setBestBeforeDate() {/**/}
}
Принцип инверсии зависимостей

“ Зависимости должны строиться


относительно абстракций, а не деталей


Принцип инверсии зависимостей
class Basket
{
public function getTotalPrice(): float
{
$total = 0;
foreach ($this->items as $item) {
$total += $item->getPrice();
}

$promoCodeProvider = new PromoCodeProvider();

if ($promoCodeProvider->isActivatedByUser($this->getUser()) === true) {


$discount = $promoCodeProvider->getDiscount($this->getUser());
$total = $total * (1 - $discount / 100.00);
}

return $total;
}
}
Принцип инверсии зависимостей
interface IDiscountProvider
{
public function isActivatedByUser(User $user);
public function getDiscount(User $user);
}

class PromoCodeProvider implements IDiscountProvider


{
public function isActivatedByUser(User $user) {/**/}
public function getDiscount(User $user) {/**/}
}
Внедрение зависимости
(Dependency Injection)

● через конструктор;
● через метод-сеттер;
● через публичное свойство.
Внедрение зависимости через
конструктор

class Basket
{
protected $discountProvider;

{
public function __construct(IDiscountProvider $discountProvider)
Подходит для
$this->discountProvider = $discountProvider; зависимостей, без
}
}
которых никак
Внедрение зависимости через метод-
сеттер

class Basket
{
protected $discountProvider;

public function setDiscountProvider(IDiscountProvider $discountProvider)


{ Подходит больше для
}
$this->discountProvider = $discountProvider;
опциональных
} зависимостей
Внедрение зависимости через
публичное свойство

class Basket
{
public $discountProvider;
}
Так делать не стоит,
/* Внешний обработчик */
$basket = new Basket();
особенно в PHP до
$basket->discountProvider = new PromoCodeProvider(); версии 7.4
Принципы GRASP

GRASP = General Responsibility


Assignment Software Patterns
(общие шаблоны распределения
ответственностей в ПО)

Craig Larman
Принципы GRASP

● Используются на этапе анализа и


проектирования системы.
● Скорее принципы, а не шаблоны
(несмотря на название).
● Имеют более обобщённый вид,
нежели SOLID.
Принципы GRASP
Слабая степень связности (Low Coupling)

Степень связности между классами характеризует то, насколько они


осведомлены о существовании друг друга и, как следствие, зависимы
друг от друга. Чем ниже эта степень между сущностями, тем они более
независимы и просты для повторного использования.
Принципы GRASP
Высокое зацепление (High cohesion)

Определяет способ и степень зависимости внутри класса. Методы в


классе должны быть максимально связаны друг с другом и выполнять
действия в рамках зоны ответственности класса.
Задание
Цель — научиться рисовать UML-диаграммы:
диаграмму классов и диаграмму
последовательности.

1. Составить полную диаграмму классов в


нотации UML для проекта, рассмотренного в
прошлом практическом задании (
https://github.com/geekbrains-web/architecture).

2. Выделить три основных, на ваш взгляд,


процесса (например, вызовы контроллера
через сервис в модель). Изобразить для них
диаграммы последовательностей.

Вам также может понравиться