SlideShare a Scribd company logo
1 of 126
Download to read offline
OVERCOMING
OBSTACLES
Программа
Состояние объекта. Mutable vs Immutable
Шаблон Immutable Object для многопоточности и запрет
наследования в нем
Карго-культ: бездумный перенос шаблона из многопоточности в
однопоточный PHP
Корректное использование final в PHP: IoC + LSP + final
Как надо использовать директиву final?
Вопрос на засыпку
Как надо использовать директиву final?
Вопрос на засыпку
1. как подсказывает твое сердце
2. везде и всегда, только final, только хардкор
3. руководствуясь здравым смыслом
Начнем
издалека
Stateful & Stateless
Объекты с состоянием и без
Stateful & Stateless
Объекты с состоянием и без
Stateful
Cat
-name: string
-breed: Breed
-furColor: string
-dateOfBirth: DateTime
-weightGram: int
-health: Health
//methods ...
Actor
Stateful & Stateless
Объекты с состоянием и без
Stateful Stateless
Cat
-name: string
-breed: Breed
-furColor: string
-dateOfBirth: DateTime
-weightGram: int
-health: Health
//methods ...
Actor
CatFeedCalculator
//noProperties
+calculateBestFoodType(Cat $cat): CatFood
+calculatePortionWeightGrams(Cat $cat): int
+calcutateWeekFeedShedule(Cat $cat): Shedule
Logic operations
Состояние объекта - это совокупность его
свойств
Stateful & Stateless
Объекты с состоянием и без
Stateful Stateless
Cat
-name: string
-breed: Breed
-furColor: string
-dateOfBirth: DateTime
-weightGram: int
-health: Health
//methods ...
CatFeedCalculator
//noProperties
+calculateBestFoodType(Cat $cat): CatFood
+calculatePortionWeightGrams(Cat $cat): int
+calcutateWeekFeedShedule(Cat $cat): Shedule
Logic operations
Actor
Multithreading
Stateful
Объекты с состоянием
Stateful
Multithreading:
Конкурирующие потоки могут одновременно
работать с одним и тем же экземпляром класса
и МЕНЯТЬ его
Actor
Stateful
Объекты с состоянием
Stateful
Multithreading:
Конкурирующие потоки могут одновременно
работать с одним и тем же экземпляром класса
и МЕНЯТЬ его
Thread_1
Actor
Stateful
Объекты с состоянием
Stateful
Thread_1
Multithreading:
Конкурирующие потоки могут одновременно
работать с одним и тем же экземпляром класса
и МЕНЯТЬ его
Actor
Stateful
Объекты с состоянием
Stateful
Thread_1 Thread_2
Multithreading:
Конкурирующие потоки могут одновременно
работать с одним и тем же экземпляром класса
и МЕНЯТЬ его
Actor
Stateful
Объекты с состоянием
Stateful
Thread_1 Thread_2
Multithreading:
Конкурирующие потоки могут одновременно
работать с одним и тем же экземпляром класса
и МЕНЯТЬ его
Actor
Stateful
Объекты с состоянием
Stateful
Thread_1 Thread_2
Multithreading:
Конкурирующие потоки могут одновременно
работать с одним и тем же экземпляром класса
и МЕНЯТЬ его
Actor
Stateful
Объекты с состоянием
Stateful
Thread_1 Thread_2
?
Где хвост?
Multithreading:
Конкурирующие потоки могут одновременно
работать с одним и тем же экземпляром класса
и МЕНЯТЬ его
Actor
Stateful
Объекты с состоянием
Stateful
Thread_1 Thread_2
?
Где хвост?
Ха-ха-ха
Multithreading:
Конкурирующие потоки могут одновременно
работать с одним и тем же экземпляром класса
и МЕНЯТЬ его
Actor
Immutable Object
Состояние объекта не может меняться после создания
Immutable Object
Состояние объекта не может меняться после создания
Запретить изменение состояния
+setAnyData(...)
Oтсутствуют не только
сеттеры, но и любые
изменения состояния
объекта
Как это
делается
Immutable Object
Состояние объекта не может меняться после создания
+setAnyData(...) and Defensive Copying
Защитное копирование
для мутабельных
свойств объектов
Запретить изменение состояния
Отсутствуют не только
сеттеры, но и любые
изменения состояния
объекта
Как это
делается
Immutable Object
Состояние объекта не может меняться после создания
+setAnyData(...) and Defensive Copying
class IAmImmutable {
private $mutableTrash;
public function __construct(MutableTrash $mutableTrash) {
$this->mutableTrash = clone $mutableTrash;
}
public function getMutableTrash(): MutableTrash {
return clone $this->mutableTrash;
}
}
Запретить изменение состояния
Oтсутствуют не только
сеттеры, но и любые
изменения состояния
объекта
Защитное копирование
для мутабельных
свойств объектов
Как это
делается
При чем тут запрет наследования?
Immutable Object
Состояние объекта не может меняться после создания
+setAnyData(...) and Defensive Copying
Запретить изменение состояния
Oтсутствуют не только
сеттеры, но и любые
изменения состояния
объекта
Защитное копирование
для мутабельных
свойств объектов
class IAmImmutable {
private $mutableTrash;
public function __construct(MutableTrash $mutableTrash) {
$this->mutableTrash = clone $mutableTrash;
}
public function getMutableTrash(): MutableTrash {
return clone $this->mutableTrash;
}
}
Immutable Object
Состояние объекта не может меняться после создания
Закрыть переопределение через Наследование
+setAnyData(...) and Defensive Copying
class IAmImmutable {
private $mutableTrash;
public function __construct(MutableTrash $mutableTrash) {
$this->mutableTrash = clone $mutableTrash;
}
public function getMutableTrash(): MutableTrash {
return clone $this->mutableTrash;
}
}
<<final>>
IAmImmutable
Запретить изменение состояния
Отсутствуют не только
сеттеры, но и любые
изменения состояния
объекта
Защитное копирование
для мутабельных
свойств объектов
<<interface>>
IAmInterface
Immutable Object
Состояние объекта не может меняться после создания
Закрыть переопределение через Наследование
+setAnyData(...) and Defensive Copying
final class IAmImmutable implements IAmInterface {
private $mutableTrash;
public function __construct(MutableTrash $mutableTrash) {
$this->mutableTrash = clone $mutableTrash;
}
public function getMutableTrash(): MutableTrash {
return clone $this->mutableTrash;
}
}
<<final>>
IAmImmutable
Запретить изменение состояния
Отсутствуют не только
сеттеры, но и любые
изменения состояния
объекта
Защитное копирование
для мутабельных
свойств объектов
<<interface>>
IAmInterface
Immutable Object
Состояние объекта не может меняться после создания
Запретить изменение состояния
Закрыть переопределение через Наследование
Зачем
Immutable Object
Состояние объекта не может меняться после создания
SeemsLikeImmutable
+doSomething(...)
Запретить изменение состояния
Закрыть переопределение через Наследование
Зачем
Not Final
Immutable Object
Состояние объекта не может меняться после создания
SeemsLikeImmutable
+doSomething(...)
//SeemsLikeImmutable client code:
public function useImmutable ( SeemsLikeImmutable $immutable) {
$immutable->doSomething();
}
Запретить изменение состояния
Закрыть переопределение через Наследование
Зачем
Not Final
Immutable Object
Состояние объекта не может меняться после создания
SeemsLikeImmutable
+doSomething(...)
EvilHeritor
+doSomething(...) // modifies internal state
Запретить изменение состояния
Закрыть переопределение через Наследование
Зачем
Not Final
//SeemsLikeImmutable client code:
public function useImmutable ( SeemsLikeImmutable $immutable) {
$immutable->doSomething();
}
Immutable Object
Состояние объекта не может меняться после создания
SeemsLikeImmutable
+doSomething(...)
EvilHeritor
+doSomething(...) // modifies internal state
Запретить изменение состояния
Закрыть переопределение через Наследование
Зачем
Not Final
//SeemsLikeImmutable client code:
public function useImmutable ( SeemsLikeImmutable $immutable) {
$immutable->doSomething();
}
Immutable Object
Состояние объекта не может меняться после создания
SeemsLikeImmutable
+doSomething(...)
EvilHeritor
//SeemsLikeImmutable client code:
public function useImmutable ( SeemsLikeImmutable $immutable) {
$immutable->doSomething();
}
Запретить изменение состояния
Закрыть переопределение через Наследование
Зачем
Not Final
+doSomething(...) // modifies internal state
Immutable Object
Состояние объекта не может меняться после создания
SeemsLikeImmutable
+doSomething(...)
EvilHeritor
//SeemsLikeImmutable client code:
public function useImmutable ( SeemsLikeImmutable $immutable) {
$immutable->doSomething();
}
Запретить изменение состояния
Закрыть переопределение через Наследование
Зачем
Mutable!
Not Final
+doSomething(...) // modifies internal state
Immutable Object
Состояние объекта не может меняться после создания
SeemsLikeImmutable
+doSomething(...)
EvilHeritor
//SeemsLikeImmutable client code:
public function useImmutable ( SeemsLikeImmutable $immutable) {
$immutable->doSomething();
}
Запретить изменение состояния
Закрыть переопределение через Наследование
Зачем
Mutable!
Not Final
+doSomething(...) // modifies internal state
Thread_2
Отрежу хвост!
Immutable Object
Состояние объекта не может меняться после создания
SeemsLikeImmutable
+doSomething(...)
EvilHeritor
//SeemsLikeImmutable client code:
public function useImmutable ( SeemsLikeImmutable $immutable) {
$immutable->doSomething();
}
Запретить изменение состояния
Закрыть переопределение через Наследование
Зачем
Mutable!
Not Final
+doSomething(...) // modifies internal state
Thread_2
Отрежу хвост!
LSP
violation risk
Immutable Object
Состояние объекта не может меняться после создания Плюсы-минусы
Immutable Object
Состояние объекта не может меняться после создания
+ Простые классы
Плюсы-минусы
Immutable Object
Состояние объекта не может меняться после создания
- Усложнение архитектуры
+ Простые классы
Плюсы-минусы
Immutable Object
Состояние объекта не может меняться после создания
- Усложнение архитектуры
+ Простые классы
+ Final+Multithread: помогает
избегать изменения состояния
объекта конкурирующими
потоками и связанными с этим
трудновоспроизводимыми
багами
Плюсы-минусы
Immutable Object
Состояние объекта не может меняться после создания
- Усложнение архитектуры
- Final сложен для понимания в
однопоточной среде
+ Простые классы
+ Final+Multithread: помогает
избегать изменения состояния
объекта конкурирующими
потоками и связанными с этим
трудновоспроизводимыми
багами
Плюсы-минусы
Зачем решать проблемы многопоточности в однопоточной среде?
Зачем решать проблемы многопоточности в однопоточной среде?
Карго культ в действии
Карго культ в действии
Java PHP
Карго культ в действии
Java PHP
- Рекомендации по проектированию собираются
из серьезных книг
Карго культ в действии
Java
- Большинство серьезных книг по дизайну пишут
джависты исходя из реалий Java
PHP
- Рекомендации по проектированию собираются
из серьезных книг
Карго культ в действии
Java
- Большинство серьезных книг по дизайну пишут
джависты исходя из реалий Java
- Реалии Java: Многопоточность
PHP
- Рекомендации по проектированию собираются
из серьезных книг
Карго культ в действии
Java
- Большинство серьезных книг по дизайну пишут
джависты исходя из реалий Java
- Реалии Java: Многопоточность
PHP
- Рекомендации по проектированию собираются
из серьезных книг
- Однопоточность
Карго культ в действии
Java
- Большинство серьезных книг по дизайну пишут
джависты исходя из реалий Java
- Реалии Java: Многопоточность
- Имеет строгие рекомендации закрывать всё
интерфейсами и финализировать
PHP
- Рекомендации по проектированию собираются
из серьезных книг
- Однопоточность
Карго культ в действии
Java
- Большинство серьезных книг по дизайну пишут
джависты исходя из реалий Java
- Реалии Java: Многопоточность
- Имеет строгие рекомендации закрывать всё
интерфейсами и финализировать
PHP
- Рекомендации по проектированию собираются
из серьезных книг
- Однопоточность
- Реалии PHP: исторически сложившаяся
“свобода” (не обязательно есть интерфейсы, не
всегда пишутся юнит-тесты)
Карго культ в действии
Java
- Большинство серьезных книг по дизайну пишут
джависты исходя из реалий Java
- Реалии Java: Многопоточность
- Имеет строгие рекомендации закрывать всё
интерфейсами и финализировать
PHP
- Рекомендации по проектированию собираются
из серьезных книг
- Однопоточность
- Реалии PHP: исторически сложившаяся
“свобода” (не обязательно есть интерфейсы, не
всегда пишутся юнит-тесты)
Все классы должны быть final
Карго культ в действии
Java
final class IAmImmutable implements IAmInterface { … }
PHP
Карго культ в действии
Java
final class IAmImmutable implements IAmInterface { … }
PHP
final class IAmImmutable implements IAmInterface { … }
или
final class IAmImmutable { … }
Карго культ в действии
Java
final class IAmImmutable implements IAmInterface { … }
PHP
final class IAmImmutable implements IAmInterface { … }
или
final class IAmImmutable { … }
Зачем тут нужна директива final?
Карго культ в действии
Java
final class IAmImmutable implements IAmInterface { … }
Final помогает решить конкретную
проблему гонки данных в условиях
многопоточности
PHP
final class IAmImmutable implements IAmInterface { … }
или
final class IAmImmutable { … }
Зачем тут нужна директива final?
Карго культ в действии
Java
final class IAmImmutable implements IAmInterface { … }
Final помогает решить конкретную
проблему гонки данных в условиях
многопоточности
PHP
final class IAmImmutable implements IAmInterface { … }
или
final class IAmImmutable { … }
“Ну чтобы другие разработчики
соблюдали дизайн”
Зачем тут нужна директива final?
Карго культ в действии
Java
final class IAmImmutable implements IAmInterface { … }
Final помогает решить конкретную
проблему гонки данных в условиях
многопоточности
PHP
final class IAmImmutable implements IAmInterface { … }
или
final class IAmImmutable { … }
“Ну чтобы другие разработчики
соблюдали дизайн”
Зачем тут нужна директива final?
Java
final class IAmImmutable implements IAmInterface { … }
Final помогает решить конкретную
проблему гонки данных в условиях
многопоточности
PHP
final class IAmImmutable implements IAmInterface { … }
или
final class IAmImmutable { … }
“Ну чтобы другие разработчики
соблюдали дизайн”
Runtime
error
prevention
Design
discussion
Карго культ в действии
Проблемы при использовании final в PHP
Проблемы при использовании final в PHP
Реалии PHP: исторически сложившаяся “свобода” (не обязательно есть
интерфейсы, не всегда пишутся юнит-тесты)
Проблемы при использовании final в PHP
Tests
Проблемы при использовании final в PHP
Tests
$pilotMock = $this->createMock(Pilot::class);
$plane = new Plane($pilotMock);
Проблемы при использовании final в PHP
Tests
$pilotMock = $this->createMock(Pilot::class);
$plane = new Plane($pilotMock);
final class Pilot {...}
Проблемы при использовании final в PHP
Tests
$pilotMock = $this->createMock(Pilot::class);
$plane = new Plane($pilotMock);
final class Pilot {...}
Проблемы при использовании final в PHP
Tests
$plane = new Plane(new Pilot(...));
final class Pilot {...}
Проблемы при использовании final в PHP
Tests
$plane = new Plane(new Pilot(...));
final class Pilot {...}
Нельзя сделать
mock!
Проблемы при использовании final в PHP
Tests
$plane = new Plane(new Pilot(...));
final class Pilot {...}
Нельзя сделать
mock!
Бетон
Проблемы при использовании final в PHP
Tests
$plane = new Plane(new Pilot(...));
final class Pilot {...}
Нельзя сделать
mock!
Бетон
ведет себя как new или static
Проблемы при использовании final в PHP
Tests
$plane = new Plane(new Pilot(...));
final class Pilot {...}
Нельзя сделать
mock!
Pilot стал неотделимой частью Plane.
Plane может быть протестирован
только вместе с Pilot.
Бетон
ведет себя как new или static
Какой принцип проектирования нарушен, что
привело к такому поведению кода?
Вопрос на засыпку
Какой принцип проектирования нарушен, что
привело к такому поведению кода?
Вопрос на засыпку
1. Принцип здравого смысла
2. Принцип инверсии зависимостей
3. Нарушено проектирование на уровне интерфейсов
Какой принцип проектирования нарушен, что
привело к такому поведению кода?
Вопрос на засыпку
1. Принцип здравого смысла
2. Принцип инверсии зависимостей
3. Нарушено проектирование на уровне интерфейсов
$plane = new Plane(new Pilot(...));
final class Pilot {...}
Проблемы при использовании final в PHP
Tests
$plane = new Plane(new Pilot(...));
final class Pilot {...}
Нельзя сделать
mock!
Pilot стал неотделимой частью Plane.
Plane может быть протестирован
только вместе с Pilot.
Бетон
ведет себя как new или static
DIP
violation
Код должен зависеть от абстракций, а не от конкретных классов
Проблемы при использовании final в PHP
Tests + Interface
Проблемы при использовании final в PHP
Tests
Нет
+ Interface
Проблемы при использовании final в PHP
Tests
Нет
final class Pilot implements Aviator {...}
+ Interface
Проблемы при использовании final в PHP
Tests
Нет
final class Pilot implements Aviator {...}
$pilotMock = $this->createMock(Aviator::class);
$plane = new Plane($pilotMock);
+ Interface
Проблемы при использовании final в PHP
Tests
Нет
final class Pilot implements Aviator {...}
$pilotMock = $this->createMock(Aviator::class);
$plane = new Plane($pilotMock);
DIP
compliant!
+ Interface
НАСТОЯЩИЙ!
ПРИМЕР
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
Внутренний метод класса, не имеет
объявления во внешнем интерфейсе.
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Внутренний метод класса, не имеет
объявления во внешнем интерфейсе.
Невозможно создать мок
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
//test code
$event = new ResponseEvent(
static::getMockForAbstractClass(HttpKernelInterface::class),
static::createMock(Request::class),
0,
$response
);
$this->listener->onKernelResponse($event);
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
//test code
$event = new ResponseEvent(
static::getMockForAbstractClass(HttpKernelInterface::class),
static::createMock(Request::class),
0,
$response
);
$this->listener->onKernelResponse($event);
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Надо разобраться во внутренних
механизмах класса вместо
создания мока
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
//test code
$event = new ResponseEvent(
static::getMockForAbstractClass(HttpKernelInterface::class),
static::createMock(Request::class),
0,
$response
);
$this->listener->onKernelResponse($event);
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Надо разобраться во внутренних
механизмах класса вместо
создания мока
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Не реализует никакой интерфейс
Но никогда не будет замокан
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Не реализует никакой интерфейс
Но никогда не будет замокан
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Не реализует никакой интерфейс
Но никогда не будет замокан
Является библиотечным классом
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Не реализует никакой интерфейс
Но никогда не будет замокан
Является библиотечным классом
Публичный метод не обьявлен во
внешнем интерфейсе
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Не реализует никакой интерфейс
Но никогда не будет замокан
Является библиотечным классом
Публичный метод не обьявлен во
внешнем интерфейсе
Должен быть замокан
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Не реализует никакой интерфейс
Но никогда не будет замокан
Является библиотечным классом
Публичный метод не обьявлен во
внешнем интерфейсе
Должен быть замокан
Как не надо делать
Пример. Final + no Interface
- Надо протестировать вновь созданный Symfony Listener
namespace SymfonyComponentHttpKernelEvent;
final class ResponseEvent extends KernelEvent
{
public function getResponse(): Response {
...
}
...
}
namespace AppInfrastructureBridgeSymfonyListener;
final class RequestListener
{
public function onKernelResponse(ResponseEvent $event): void {
$this->logResponse($event->getResponse());
}
...
}
Не реализует никакой интерфейс
Но никогда не будет замокан
Нет понятной причины для
использования final
Является библиотечным классом
Публичный метод не обьявлен во
внешнем интерфейсе
Должен быть замокан
?
Как не надо делать
Так нужен ли final в PHP или нет?
1. Скорее нужен, чем не нужен
2. Скорее не нужен, чем нужен
3. Очень нужен для IoC + LSP + final
Так нужен ли final в PHP или нет?
IoC + LSP + final
IoC + LSP + final
AbstractAircraft
Plane
Helicopter
AirBalloon
IoC + LSP + final
AbstractAircraft
Plane
Helicopter
AirBalloon
abstract class AbstractAircraft
{
public function aviate()
{
$this->prepareToFly();
$this->fly();
$this->reportOk();
}
}
IoC + LSP + final
abstract class AbstractAircraft
{
public function aviate()
{
$this->prepareToFly();
$this->fly();
$this->reportOk();
}
abstract protected function prepareToFly();
abstract protected function fly();
abstract protected function reportOk();
}
AbstractAircraft
Plane
Helicopter
AirBalloon
IoC + LSP + final
abstract class AbstractAircraft
{
public function aviate()
{
$this->prepareToFly();
$this->fly();
$this->reportOk();
}
abstract protected function prepareToFly();
abstract protected function fly();
abstract protected function reportOk();
}
AbstractAircraft
Plane
Helicopter
AirBalloon
Hollywood principle
(IoC)
IoC + LSP + final
abstract class AbstractAircraft
{
public function aviate()
{
$this->prepareToFly();
$this->fly();
$this->reportOk();
}
abstract protected function prepareToFly();
abstract protected function fly();
abstract protected function reportOk();
}
AbstractAircraft
Plane
Helicopter
AirBalloon
Hollywood principle
(IoC)
Наследники могут переопределять
поведение базового класса
IoC + LSP + final
abstract class AbstractAircraft
{
final public function aviate()
{
$this->prepareToFly();
$this->fly();
$this->reportOk();
}
abstract protected function prepareToFly();
abstract protected function fly();
abstract protected function reportOk();
}
AbstractAircraft
Plane
Helicopter
AirBalloon
Hollywood principle
(IoC)
Наследники могут переопределять
поведение базового класса
IoC + LSP + final
abstract class AbstractAircraft
{
final public function aviate()
{
$this->prepareToFly();
$this->fly();
$this->reportOk();
}
abstract protected function prepareToFly();
abstract protected function fly();
abstract protected function reportOk();
}
AbstractAircraft
Plane
Helicopter
AirBalloon
Hollywood principle
(IoC)
LSP compliant
Что здесь не так?
abstract class AbstractAircraft
{
final public function aviate()
{
$this->prepareToFly();
$this->fly();
$this->reportOk();
}
abstract protected function prepareToFly();
abstract protected function fly();
abstract protected function reportOk();
}
AbstractAircraft
Plane
Helicopter
AirBalloon
Hollywood principle
(IoC)
LSP compliant
Нужен интерфейс!
abstract class AbstractAircraft
{
final public function aviate()
{
$this->prepareToFly();
$this->fly();
$this->reportOk();
}
abstract protected function prepareToFly();
abstract protected function fly();
abstract protected function reportOk();
}
AbstractAircraft
Plane
Helicopter
AirBalloon
Hollywood principle
(IoC)
LSP compliant
Бетон!
Не мокается!
Так нужен ли final в PHP или нет?
1. Скорее нужен, чем не нужен
2. Скорее не нужен, чем нужен
3. Очень нужен для IoC + LSP + final
Так нужен ли final в PHP или нет?
1. Скорее нужен, чем не нужен
2. Скорее не нужен, чем нужен
3. Очень нужен для IoC + LSP + final
Зависит от
архитектурного
стиля
Архитектурный стиль + final
Архитектурный стиль + final
Java-like
Архитектурный стиль + final
Java-like oldschool PHP
Архитектурный стиль + final
Java-like oldschool PHP
Закрывать все
final + Interface
Без final, Interface по
необходимости
Архитектурный стиль + final
Java-like oldschool PHP
Закрывать все
final + Interface
Без final, Interface по
необходимости
+
Что почитать?
Мои статьи:
Уровни абстракций
Принцип инверсии зависимостей
Принцип подстановки Барбары Лисков
Голливудский принцип (IoC)
Что почитать?
Мои статьи:
Уровни абстракций
Принцип инверсии зависимостей
Принцип подстановки Барбары Лисков
Голливудский принцип (IoC)
Не мои статьи:
Гонка данных
Книжка:
Паттерны проектирования O’reilly
Что почитать?
Мои контакты
О серьезном писать сюда: https://www.linkedin.com/in/natalynyshta/
(или сюда amriwman@gmail.com)
О несерьезном сюда: https://www.facebook.com/amriwman/
Благодарности
Александра Григорова
Александр Тромса
Андрей Кузеро
Артем Дударев
Дмитрий Прибитков
Евгений Мартюшев
Михаил Чернышев
Ольга Артюх
Tom
"Analysis and Design Principles within OOP. Inheritance forbiddance problem in a singlethreading PHP", Natalie Nyshta

More Related Content

More from Fwdays

More from Fwdays (20)

"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y..."How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
 
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro Spodarets"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro Spodarets
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
 
"Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl..."Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl...
 
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T..."How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
 
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ..."The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
 
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu..."[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
 
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care..."[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
 
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"..."4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
 
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast..."Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
 
"Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others..."Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others...
 
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
 
"Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv..."Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv...
 
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin..."How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
 

Recently uploaded

СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdfСИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
Хроники кибер-безопасника
 
Cyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdfCyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdf
Хроники кибер-безопасника
 
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Ирония безопасности
 
CVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdfCVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdf
Хроники кибер-безопасника
 
2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf
Хроники кибер-безопасника
 
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
Ирония безопасности
 

Recently uploaded (9)

СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdfСИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
 
Cyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdfCyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdf
 
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
 
CVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdfCVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdf
 
2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf
 
MS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdfMS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdf
 
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
 
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdfMalware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
 
Ransomware_Q3 2023. The report [RU].pdf
Ransomware_Q3 2023.  The report [RU].pdfRansomware_Q3 2023.  The report [RU].pdf
Ransomware_Q3 2023. The report [RU].pdf
 

"Analysis and Design Principles within OOP. Inheritance forbiddance problem in a singlethreading PHP", Natalie Nyshta

  • 1.
  • 3. Программа Состояние объекта. Mutable vs Immutable Шаблон Immutable Object для многопоточности и запрет наследования в нем Карго-культ: бездумный перенос шаблона из многопоточности в однопоточный PHP Корректное использование final в PHP: IoC + LSP + final
  • 4. Как надо использовать директиву final? Вопрос на засыпку
  • 5. Как надо использовать директиву final? Вопрос на засыпку 1. как подсказывает твое сердце 2. везде и всегда, только final, только хардкор 3. руководствуясь здравым смыслом
  • 7. Stateful & Stateless Объекты с состоянием и без
  • 8. Stateful & Stateless Объекты с состоянием и без Stateful Cat -name: string -breed: Breed -furColor: string -dateOfBirth: DateTime -weightGram: int -health: Health //methods ... Actor
  • 9. Stateful & Stateless Объекты с состоянием и без Stateful Stateless Cat -name: string -breed: Breed -furColor: string -dateOfBirth: DateTime -weightGram: int -health: Health //methods ... Actor CatFeedCalculator //noProperties +calculateBestFoodType(Cat $cat): CatFood +calculatePortionWeightGrams(Cat $cat): int +calcutateWeekFeedShedule(Cat $cat): Shedule Logic operations
  • 10. Состояние объекта - это совокупность его свойств
  • 11. Stateful & Stateless Объекты с состоянием и без Stateful Stateless Cat -name: string -breed: Breed -furColor: string -dateOfBirth: DateTime -weightGram: int -health: Health //methods ... CatFeedCalculator //noProperties +calculateBestFoodType(Cat $cat): CatFood +calculatePortionWeightGrams(Cat $cat): int +calcutateWeekFeedShedule(Cat $cat): Shedule Logic operations Actor
  • 12.
  • 14. Stateful Объекты с состоянием Stateful Multithreading: Конкурирующие потоки могут одновременно работать с одним и тем же экземпляром класса и МЕНЯТЬ его Actor
  • 15. Stateful Объекты с состоянием Stateful Multithreading: Конкурирующие потоки могут одновременно работать с одним и тем же экземпляром класса и МЕНЯТЬ его Thread_1 Actor
  • 16. Stateful Объекты с состоянием Stateful Thread_1 Multithreading: Конкурирующие потоки могут одновременно работать с одним и тем же экземпляром класса и МЕНЯТЬ его Actor
  • 17. Stateful Объекты с состоянием Stateful Thread_1 Thread_2 Multithreading: Конкурирующие потоки могут одновременно работать с одним и тем же экземпляром класса и МЕНЯТЬ его Actor
  • 18. Stateful Объекты с состоянием Stateful Thread_1 Thread_2 Multithreading: Конкурирующие потоки могут одновременно работать с одним и тем же экземпляром класса и МЕНЯТЬ его Actor
  • 19. Stateful Объекты с состоянием Stateful Thread_1 Thread_2 Multithreading: Конкурирующие потоки могут одновременно работать с одним и тем же экземпляром класса и МЕНЯТЬ его Actor
  • 20. Stateful Объекты с состоянием Stateful Thread_1 Thread_2 ? Где хвост? Multithreading: Конкурирующие потоки могут одновременно работать с одним и тем же экземпляром класса и МЕНЯТЬ его Actor
  • 21. Stateful Объекты с состоянием Stateful Thread_1 Thread_2 ? Где хвост? Ха-ха-ха Multithreading: Конкурирующие потоки могут одновременно работать с одним и тем же экземпляром класса и МЕНЯТЬ его Actor
  • 22.
  • 23. Immutable Object Состояние объекта не может меняться после создания
  • 24. Immutable Object Состояние объекта не может меняться после создания Запретить изменение состояния +setAnyData(...) Oтсутствуют не только сеттеры, но и любые изменения состояния объекта Как это делается
  • 25. Immutable Object Состояние объекта не может меняться после создания +setAnyData(...) and Defensive Copying Защитное копирование для мутабельных свойств объектов Запретить изменение состояния Отсутствуют не только сеттеры, но и любые изменения состояния объекта Как это делается
  • 26. Immutable Object Состояние объекта не может меняться после создания +setAnyData(...) and Defensive Copying class IAmImmutable { private $mutableTrash; public function __construct(MutableTrash $mutableTrash) { $this->mutableTrash = clone $mutableTrash; } public function getMutableTrash(): MutableTrash { return clone $this->mutableTrash; } } Запретить изменение состояния Oтсутствуют не только сеттеры, но и любые изменения состояния объекта Защитное копирование для мутабельных свойств объектов Как это делается
  • 27. При чем тут запрет наследования?
  • 28. Immutable Object Состояние объекта не может меняться после создания +setAnyData(...) and Defensive Copying Запретить изменение состояния Oтсутствуют не только сеттеры, но и любые изменения состояния объекта Защитное копирование для мутабельных свойств объектов class IAmImmutable { private $mutableTrash; public function __construct(MutableTrash $mutableTrash) { $this->mutableTrash = clone $mutableTrash; } public function getMutableTrash(): MutableTrash { return clone $this->mutableTrash; } }
  • 29. Immutable Object Состояние объекта не может меняться после создания Закрыть переопределение через Наследование +setAnyData(...) and Defensive Copying class IAmImmutable { private $mutableTrash; public function __construct(MutableTrash $mutableTrash) { $this->mutableTrash = clone $mutableTrash; } public function getMutableTrash(): MutableTrash { return clone $this->mutableTrash; } } <<final>> IAmImmutable Запретить изменение состояния Отсутствуют не только сеттеры, но и любые изменения состояния объекта Защитное копирование для мутабельных свойств объектов <<interface>> IAmInterface
  • 30. Immutable Object Состояние объекта не может меняться после создания Закрыть переопределение через Наследование +setAnyData(...) and Defensive Copying final class IAmImmutable implements IAmInterface { private $mutableTrash; public function __construct(MutableTrash $mutableTrash) { $this->mutableTrash = clone $mutableTrash; } public function getMutableTrash(): MutableTrash { return clone $this->mutableTrash; } } <<final>> IAmImmutable Запретить изменение состояния Отсутствуют не только сеттеры, но и любые изменения состояния объекта Защитное копирование для мутабельных свойств объектов <<interface>> IAmInterface
  • 31. Immutable Object Состояние объекта не может меняться после создания Запретить изменение состояния Закрыть переопределение через Наследование Зачем
  • 32. Immutable Object Состояние объекта не может меняться после создания SeemsLikeImmutable +doSomething(...) Запретить изменение состояния Закрыть переопределение через Наследование Зачем Not Final
  • 33. Immutable Object Состояние объекта не может меняться после создания SeemsLikeImmutable +doSomething(...) //SeemsLikeImmutable client code: public function useImmutable ( SeemsLikeImmutable $immutable) { $immutable->doSomething(); } Запретить изменение состояния Закрыть переопределение через Наследование Зачем Not Final
  • 34. Immutable Object Состояние объекта не может меняться после создания SeemsLikeImmutable +doSomething(...) EvilHeritor +doSomething(...) // modifies internal state Запретить изменение состояния Закрыть переопределение через Наследование Зачем Not Final //SeemsLikeImmutable client code: public function useImmutable ( SeemsLikeImmutable $immutable) { $immutable->doSomething(); }
  • 35. Immutable Object Состояние объекта не может меняться после создания SeemsLikeImmutable +doSomething(...) EvilHeritor +doSomething(...) // modifies internal state Запретить изменение состояния Закрыть переопределение через Наследование Зачем Not Final //SeemsLikeImmutable client code: public function useImmutable ( SeemsLikeImmutable $immutable) { $immutable->doSomething(); }
  • 36. Immutable Object Состояние объекта не может меняться после создания SeemsLikeImmutable +doSomething(...) EvilHeritor //SeemsLikeImmutable client code: public function useImmutable ( SeemsLikeImmutable $immutable) { $immutable->doSomething(); } Запретить изменение состояния Закрыть переопределение через Наследование Зачем Not Final +doSomething(...) // modifies internal state
  • 37. Immutable Object Состояние объекта не может меняться после создания SeemsLikeImmutable +doSomething(...) EvilHeritor //SeemsLikeImmutable client code: public function useImmutable ( SeemsLikeImmutable $immutable) { $immutable->doSomething(); } Запретить изменение состояния Закрыть переопределение через Наследование Зачем Mutable! Not Final +doSomething(...) // modifies internal state
  • 38. Immutable Object Состояние объекта не может меняться после создания SeemsLikeImmutable +doSomething(...) EvilHeritor //SeemsLikeImmutable client code: public function useImmutable ( SeemsLikeImmutable $immutable) { $immutable->doSomething(); } Запретить изменение состояния Закрыть переопределение через Наследование Зачем Mutable! Not Final +doSomething(...) // modifies internal state Thread_2 Отрежу хвост!
  • 39. Immutable Object Состояние объекта не может меняться после создания SeemsLikeImmutable +doSomething(...) EvilHeritor //SeemsLikeImmutable client code: public function useImmutable ( SeemsLikeImmutable $immutable) { $immutable->doSomething(); } Запретить изменение состояния Закрыть переопределение через Наследование Зачем Mutable! Not Final +doSomething(...) // modifies internal state Thread_2 Отрежу хвост! LSP violation risk
  • 40. Immutable Object Состояние объекта не может меняться после создания Плюсы-минусы
  • 41. Immutable Object Состояние объекта не может меняться после создания + Простые классы Плюсы-минусы
  • 42. Immutable Object Состояние объекта не может меняться после создания - Усложнение архитектуры + Простые классы Плюсы-минусы
  • 43. Immutable Object Состояние объекта не может меняться после создания - Усложнение архитектуры + Простые классы + Final+Multithread: помогает избегать изменения состояния объекта конкурирующими потоками и связанными с этим трудновоспроизводимыми багами Плюсы-минусы
  • 44. Immutable Object Состояние объекта не может меняться после создания - Усложнение архитектуры - Final сложен для понимания в однопоточной среде + Простые классы + Final+Multithread: помогает избегать изменения состояния объекта конкурирующими потоками и связанными с этим трудновоспроизводимыми багами Плюсы-минусы
  • 45. Зачем решать проблемы многопоточности в однопоточной среде?
  • 46. Зачем решать проблемы многопоточности в однопоточной среде?
  • 47. Карго культ в действии
  • 48. Карго культ в действии Java PHP
  • 49. Карго культ в действии Java PHP - Рекомендации по проектированию собираются из серьезных книг
  • 50. Карго культ в действии Java - Большинство серьезных книг по дизайну пишут джависты исходя из реалий Java PHP - Рекомендации по проектированию собираются из серьезных книг
  • 51. Карго культ в действии Java - Большинство серьезных книг по дизайну пишут джависты исходя из реалий Java - Реалии Java: Многопоточность PHP - Рекомендации по проектированию собираются из серьезных книг
  • 52. Карго культ в действии Java - Большинство серьезных книг по дизайну пишут джависты исходя из реалий Java - Реалии Java: Многопоточность PHP - Рекомендации по проектированию собираются из серьезных книг - Однопоточность
  • 53. Карго культ в действии Java - Большинство серьезных книг по дизайну пишут джависты исходя из реалий Java - Реалии Java: Многопоточность - Имеет строгие рекомендации закрывать всё интерфейсами и финализировать PHP - Рекомендации по проектированию собираются из серьезных книг - Однопоточность
  • 54. Карго культ в действии Java - Большинство серьезных книг по дизайну пишут джависты исходя из реалий Java - Реалии Java: Многопоточность - Имеет строгие рекомендации закрывать всё интерфейсами и финализировать PHP - Рекомендации по проектированию собираются из серьезных книг - Однопоточность - Реалии PHP: исторически сложившаяся “свобода” (не обязательно есть интерфейсы, не всегда пишутся юнит-тесты)
  • 55. Карго культ в действии Java - Большинство серьезных книг по дизайну пишут джависты исходя из реалий Java - Реалии Java: Многопоточность - Имеет строгие рекомендации закрывать всё интерфейсами и финализировать PHP - Рекомендации по проектированию собираются из серьезных книг - Однопоточность - Реалии PHP: исторически сложившаяся “свобода” (не обязательно есть интерфейсы, не всегда пишутся юнит-тесты) Все классы должны быть final
  • 56. Карго культ в действии Java final class IAmImmutable implements IAmInterface { … } PHP
  • 57. Карго культ в действии Java final class IAmImmutable implements IAmInterface { … } PHP final class IAmImmutable implements IAmInterface { … } или final class IAmImmutable { … }
  • 58. Карго культ в действии Java final class IAmImmutable implements IAmInterface { … } PHP final class IAmImmutable implements IAmInterface { … } или final class IAmImmutable { … } Зачем тут нужна директива final?
  • 59. Карго культ в действии Java final class IAmImmutable implements IAmInterface { … } Final помогает решить конкретную проблему гонки данных в условиях многопоточности PHP final class IAmImmutable implements IAmInterface { … } или final class IAmImmutable { … } Зачем тут нужна директива final?
  • 60. Карго культ в действии Java final class IAmImmutable implements IAmInterface { … } Final помогает решить конкретную проблему гонки данных в условиях многопоточности PHP final class IAmImmutable implements IAmInterface { … } или final class IAmImmutable { … } “Ну чтобы другие разработчики соблюдали дизайн” Зачем тут нужна директива final?
  • 61. Карго культ в действии Java final class IAmImmutable implements IAmInterface { … } Final помогает решить конкретную проблему гонки данных в условиях многопоточности PHP final class IAmImmutable implements IAmInterface { … } или final class IAmImmutable { … } “Ну чтобы другие разработчики соблюдали дизайн” Зачем тут нужна директива final?
  • 62. Java final class IAmImmutable implements IAmInterface { … } Final помогает решить конкретную проблему гонки данных в условиях многопоточности PHP final class IAmImmutable implements IAmInterface { … } или final class IAmImmutable { … } “Ну чтобы другие разработчики соблюдали дизайн” Runtime error prevention Design discussion Карго культ в действии
  • 64. Проблемы при использовании final в PHP Реалии PHP: исторически сложившаяся “свобода” (не обязательно есть интерфейсы, не всегда пишутся юнит-тесты)
  • 66. Проблемы при использовании final в PHP Tests $pilotMock = $this->createMock(Pilot::class); $plane = new Plane($pilotMock);
  • 67. Проблемы при использовании final в PHP Tests $pilotMock = $this->createMock(Pilot::class); $plane = new Plane($pilotMock); final class Pilot {...}
  • 68. Проблемы при использовании final в PHP Tests $pilotMock = $this->createMock(Pilot::class); $plane = new Plane($pilotMock); final class Pilot {...}
  • 69. Проблемы при использовании final в PHP Tests $plane = new Plane(new Pilot(...)); final class Pilot {...}
  • 70. Проблемы при использовании final в PHP Tests $plane = new Plane(new Pilot(...)); final class Pilot {...} Нельзя сделать mock!
  • 71. Проблемы при использовании final в PHP Tests $plane = new Plane(new Pilot(...)); final class Pilot {...} Нельзя сделать mock! Бетон
  • 72. Проблемы при использовании final в PHP Tests $plane = new Plane(new Pilot(...)); final class Pilot {...} Нельзя сделать mock! Бетон ведет себя как new или static
  • 73. Проблемы при использовании final в PHP Tests $plane = new Plane(new Pilot(...)); final class Pilot {...} Нельзя сделать mock! Pilot стал неотделимой частью Plane. Plane может быть протестирован только вместе с Pilot. Бетон ведет себя как new или static
  • 74.
  • 75. Какой принцип проектирования нарушен, что привело к такому поведению кода? Вопрос на засыпку
  • 76. Какой принцип проектирования нарушен, что привело к такому поведению кода? Вопрос на засыпку 1. Принцип здравого смысла 2. Принцип инверсии зависимостей 3. Нарушено проектирование на уровне интерфейсов
  • 77. Какой принцип проектирования нарушен, что привело к такому поведению кода? Вопрос на засыпку 1. Принцип здравого смысла 2. Принцип инверсии зависимостей 3. Нарушено проектирование на уровне интерфейсов $plane = new Plane(new Pilot(...)); final class Pilot {...}
  • 78. Проблемы при использовании final в PHP Tests $plane = new Plane(new Pilot(...)); final class Pilot {...} Нельзя сделать mock! Pilot стал неотделимой частью Plane. Plane может быть протестирован только вместе с Pilot. Бетон ведет себя как new или static DIP violation Код должен зависеть от абстракций, а не от конкретных классов
  • 79. Проблемы при использовании final в PHP Tests + Interface
  • 80. Проблемы при использовании final в PHP Tests Нет + Interface
  • 81. Проблемы при использовании final в PHP Tests Нет final class Pilot implements Aviator {...} + Interface
  • 82. Проблемы при использовании final в PHP Tests Нет final class Pilot implements Aviator {...} $pilotMock = $this->createMock(Aviator::class); $plane = new Plane($pilotMock); + Interface
  • 83. Проблемы при использовании final в PHP Tests Нет final class Pilot implements Aviator {...} $pilotMock = $this->createMock(Aviator::class); $plane = new Plane($pilotMock); DIP compliant! + Interface
  • 85. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener
  • 86. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener Как не надо делать
  • 87. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Как не надо делать
  • 88. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Как не надо делать
  • 89. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } Как не надо делать
  • 90. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } Внутренний метод класса, не имеет объявления во внешнем интерфейсе. Как не надо делать
  • 91. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Внутренний метод класса, не имеет объявления во внешнем интерфейсе. Невозможно создать мок Как не надо делать
  • 92. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener //test code $event = new ResponseEvent( static::getMockForAbstractClass(HttpKernelInterface::class), static::createMock(Request::class), 0, $response ); $this->listener->onKernelResponse($event); namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Как не надо делать
  • 93. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener //test code $event = new ResponseEvent( static::getMockForAbstractClass(HttpKernelInterface::class), static::createMock(Request::class), 0, $response ); $this->listener->onKernelResponse($event); namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Надо разобраться во внутренних механизмах класса вместо создания мока Как не надо делать
  • 94. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener //test code $event = new ResponseEvent( static::getMockForAbstractClass(HttpKernelInterface::class), static::createMock(Request::class), 0, $response ); $this->listener->onKernelResponse($event); namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Надо разобраться во внутренних механизмах класса вместо создания мока Как не надо делать
  • 95. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Не реализует никакой интерфейс Но никогда не будет замокан Как не надо делать
  • 96. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Не реализует никакой интерфейс Но никогда не будет замокан Как не надо делать
  • 97. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Не реализует никакой интерфейс Но никогда не будет замокан Является библиотечным классом Как не надо делать
  • 98. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Не реализует никакой интерфейс Но никогда не будет замокан Является библиотечным классом Публичный метод не обьявлен во внешнем интерфейсе Как не надо делать
  • 99. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Не реализует никакой интерфейс Но никогда не будет замокан Является библиотечным классом Публичный метод не обьявлен во внешнем интерфейсе Должен быть замокан Как не надо делать
  • 100. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Не реализует никакой интерфейс Но никогда не будет замокан Является библиотечным классом Публичный метод не обьявлен во внешнем интерфейсе Должен быть замокан Как не надо делать
  • 101. Пример. Final + no Interface - Надо протестировать вновь созданный Symfony Listener namespace SymfonyComponentHttpKernelEvent; final class ResponseEvent extends KernelEvent { public function getResponse(): Response { ... } ... } namespace AppInfrastructureBridgeSymfonyListener; final class RequestListener { public function onKernelResponse(ResponseEvent $event): void { $this->logResponse($event->getResponse()); } ... } Не реализует никакой интерфейс Но никогда не будет замокан Нет понятной причины для использования final Является библиотечным классом Публичный метод не обьявлен во внешнем интерфейсе Должен быть замокан ? Как не надо делать
  • 102. Так нужен ли final в PHP или нет?
  • 103. 1. Скорее нужен, чем не нужен 2. Скорее не нужен, чем нужен 3. Очень нужен для IoC + LSP + final Так нужен ли final в PHP или нет?
  • 104. IoC + LSP + final
  • 105. IoC + LSP + final AbstractAircraft Plane Helicopter AirBalloon
  • 106. IoC + LSP + final AbstractAircraft Plane Helicopter AirBalloon abstract class AbstractAircraft { public function aviate() { $this->prepareToFly(); $this->fly(); $this->reportOk(); } }
  • 107. IoC + LSP + final abstract class AbstractAircraft { public function aviate() { $this->prepareToFly(); $this->fly(); $this->reportOk(); } abstract protected function prepareToFly(); abstract protected function fly(); abstract protected function reportOk(); } AbstractAircraft Plane Helicopter AirBalloon
  • 108. IoC + LSP + final abstract class AbstractAircraft { public function aviate() { $this->prepareToFly(); $this->fly(); $this->reportOk(); } abstract protected function prepareToFly(); abstract protected function fly(); abstract protected function reportOk(); } AbstractAircraft Plane Helicopter AirBalloon Hollywood principle (IoC)
  • 109. IoC + LSP + final abstract class AbstractAircraft { public function aviate() { $this->prepareToFly(); $this->fly(); $this->reportOk(); } abstract protected function prepareToFly(); abstract protected function fly(); abstract protected function reportOk(); } AbstractAircraft Plane Helicopter AirBalloon Hollywood principle (IoC) Наследники могут переопределять поведение базового класса
  • 110. IoC + LSP + final abstract class AbstractAircraft { final public function aviate() { $this->prepareToFly(); $this->fly(); $this->reportOk(); } abstract protected function prepareToFly(); abstract protected function fly(); abstract protected function reportOk(); } AbstractAircraft Plane Helicopter AirBalloon Hollywood principle (IoC) Наследники могут переопределять поведение базового класса
  • 111. IoC + LSP + final abstract class AbstractAircraft { final public function aviate() { $this->prepareToFly(); $this->fly(); $this->reportOk(); } abstract protected function prepareToFly(); abstract protected function fly(); abstract protected function reportOk(); } AbstractAircraft Plane Helicopter AirBalloon Hollywood principle (IoC) LSP compliant
  • 112. Что здесь не так? abstract class AbstractAircraft { final public function aviate() { $this->prepareToFly(); $this->fly(); $this->reportOk(); } abstract protected function prepareToFly(); abstract protected function fly(); abstract protected function reportOk(); } AbstractAircraft Plane Helicopter AirBalloon Hollywood principle (IoC) LSP compliant
  • 113. Нужен интерфейс! abstract class AbstractAircraft { final public function aviate() { $this->prepareToFly(); $this->fly(); $this->reportOk(); } abstract protected function prepareToFly(); abstract protected function fly(); abstract protected function reportOk(); } AbstractAircraft Plane Helicopter AirBalloon Hollywood principle (IoC) LSP compliant Бетон! Не мокается!
  • 114. Так нужен ли final в PHP или нет? 1. Скорее нужен, чем не нужен 2. Скорее не нужен, чем нужен 3. Очень нужен для IoC + LSP + final
  • 115. Так нужен ли final в PHP или нет? 1. Скорее нужен, чем не нужен 2. Скорее не нужен, чем нужен 3. Очень нужен для IoC + LSP + final Зависит от архитектурного стиля
  • 118. Архитектурный стиль + final Java-like oldschool PHP
  • 119. Архитектурный стиль + final Java-like oldschool PHP Закрывать все final + Interface Без final, Interface по необходимости
  • 120. Архитектурный стиль + final Java-like oldschool PHP Закрывать все final + Interface Без final, Interface по необходимости +
  • 122. Мои статьи: Уровни абстракций Принцип инверсии зависимостей Принцип подстановки Барбары Лисков Голливудский принцип (IoC) Что почитать?
  • 123. Мои статьи: Уровни абстракций Принцип инверсии зависимостей Принцип подстановки Барбары Лисков Голливудский принцип (IoC) Не мои статьи: Гонка данных Книжка: Паттерны проектирования O’reilly Что почитать?
  • 124. Мои контакты О серьезном писать сюда: https://www.linkedin.com/in/natalynyshta/ (или сюда amriwman@gmail.com) О несерьезном сюда: https://www.facebook.com/amriwman/
  • 125. Благодарности Александра Григорова Александр Тромса Андрей Кузеро Артем Дударев Дмитрий Прибитков Евгений Мартюшев Михаил Чернышев Ольга Артюх Tom