Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
S.O.L.I.D.
Принципы
объектного
проектирования
классов
Обо мне
Alexander Nemanov
alexander@asoft4web.com
Skype: asoft4web
FB:
https://www.facebook.com/alexander.nemanov
S.O.L.I.D. - принципы
1. Принцип единственности ответственности (SRP: Single Responsibility
Principle)
2. Принцип открытос...
SRP - Принцип единственности ответственности
Формулировка:
Не должно быть больше одной причины для изменения класса
Каждый...
SRP - Принцип единственности ответственности
class Order
{
public function calculateTotalSum() {/*...*/}
public function g...
SRP - Принцип единственности ответственности
3 различный типов задач (3-и причины для изменения одного класса):
• работа с...
SRP - Принцип единственности ответственности
class Order {
public function calculateTotalSum() {/*...*/}
public function g...
SRP - Принцип единственности ответственности
Проблема: Задача валидации данных
Первое решение:
class Product {
public func...
OCP - Принцип открытости/закрытости
Формулировка:
• программные сущности (классы, модули, функции и т.д.) должны быть
откр...
OCP - Принцип открытости/закрытости
class Logger {
public function Log($logText) { /* Save to file */ }
}
class SmtpMailer...
OCP - Принцип открытости/закрытости
class DbLogger {
public function Log($logText) { /* Save to Db */ }
}
class SmtpMailer...
OCP - Принцип открытости/закрытости
interface LoggerInterface {
public function Log($logText);
}
class Logger implements L...
OCP - Принцип открытости/закрытости
class SmtpMailer {
private $logger;
public function __construct(LoggerInterface $logge...
OCP - Принцип открытости/закрытости
Конкретизируя классы методом instanceof мы должны сразу понять, что наш
код начал "поп...
OCP - Принцип открытости/закрытости
1. "Открыт для расширения":
поведение может быть расширено
путем добавления новых объе...
LSP - Принцип замещения Лисков
Формулировка №1: eсли для каждого объекта o1 типа S существует объект
o2 типа T, который дл...
LSP - Принцип замещения Лисков
Формулировка №1: eсли для каждого объекта o1 типа S существует объект
o2 типа T, который дл...
LSP - Принцип замещения Лисков
Поведение наследуемых классов не должно противоречить поведению,
заданному базовым классом,...
LSP - Принцип замещения Лисков
• Следовать этому принципу очень важно при проектировании новых типов
с использованием насл...
ISP - Принцип разделения интерфейса
Формулировка: клиенты не должны зависеть от методов, которые они не
используют
Как и п...
ISP - Принцип разделения интерфейса
interface IItem {
public function applyDiscount($discount);
public function applyPromo...
ISP - Принцип разделения интерфейса
interface IItem {
public function setCondition($condition);
public function setPrice($...
ISP - Принцип разделения интерфейса
class Book implemets IItem, IDiscountable {
public function setCondition($condition){/...
DIP - Принцип инверсии зависимости
Формулировка:
• Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Об...
DIP - Принцип инверсии зависимости
class OrderProcess {
public function CalculeteTotal(Order $order) {
$itemTotal = $order...
DIP - Принцип инверсии зависимости
Причина, по которой проекты "стареют", заключается в том, что у
разработчиков нет возмо...
DIP - Принцип инверсии зависимости
Перечислим все обязанности, которые выполняет класс OrderProcessor:
• Знает, как вычисл...
DIP - Принцип инверсии зависимости
interface DiscountCalculatorInterface {
public function calculateDiscount(Order $order)...
DIP - Принцип инверсии зависимости
class OrderProcess {
/** @var DiscountCalculatorInterface */
private $discountCalculato...
DIP - Принцип инверсии зависимости
OrderProcess
DiscountCalculator
DiscountCalculatorInterface TaxStrategyInterface
USTaxS...
DIP - Принцип инверсии зависимости
Принцип обращения зависимости - это очень мощный
инструмент, который в сочетании с друг...
?
SOLID
Upcoming SlideShare
Loading in …5
×

SOLID

1,079 views

Published on

S.O.L.I.D. - Принципы объектного проектирования классов

Published in: Technology
  • Be the first to comment

SOLID

  1. 1. S.O.L.I.D. Принципы объектного проектирования классов
  2. 2. Обо мне Alexander Nemanov alexander@asoft4web.com Skype: asoft4web FB: https://www.facebook.com/alexander.nemanov
  3. 3. S.O.L.I.D. - принципы 1. Принцип единственности ответственности (SRP: Single Responsibility Principle) 2. Принцип открытости/закрытости (OCP: Open/Closed Principle) 3. Принцип подстановки Лисков (LSP: Liskov Substitution Principle) 4. Принцип разделения интерфейса (ISP: Interface Segregation Principle) 5. Принцип инверсии зависимостей (DIP: Dependency Inversion Principle)
  4. 4. SRP - Принцип единственности ответственности Формулировка: Не должно быть больше одной причины для изменения класса Каждый объект должен иметь одну обязанность и эта обязанность должна быть полностью инкапсулирована в класс.
  5. 5. SRP - Принцип единственности ответственности class Order { public function calculateTotalSum() {/*...*/} public function getItems() {/*...*/} public function getItemCount() {/*...*/} public function addItem($item) {/*...*/} public function deleteItem($item) {/*...*/} public function printOrder() {/*...*/} public function showOrder() {/*...*/} public function load() {/*...*/} public function save() {/*...*/} public function update() {/*...*/} public function delete() {/*...*/} }
  6. 6. SRP - Принцип единственности ответственности 3 различный типов задач (3-и причины для изменения одного класса): • работа с самим заказом • отображение заказа • работа с хранилищем данных Решение: Сделать отдельные классы. Чтобы каждый класс занимается своей конкретной задачей и для каждого класса была только 1 причина для его изменения.
  7. 7. SRP - Принцип единственности ответственности class Order { public function calculateTotalSum() {/*...*/} public function getItems() {/*...*/} public function getItemCount() {/*...*/} public function addItem($item) {/*...*/} public function deleteItem($item) {/*...*/} } class OrderRepository { public function load($orderID){/*...*/} public function save($order){/*...*/} public function update($order){/*...*/} public function delete($order){/*...*/} } class OrderViewer { public function printOrder($order){/*...*/} public function showOrder($order){/*...*/} }
  8. 8. SRP - Принцип единственности ответственности Проблема: Задача валидации данных Первое решение: class Product { public function isValid() { return $this->price > 0; } } Решение: отдать ответственность за валидацию данных продукта другому объекту. Причем надо сделать так, чтобы сам объект продукта не зависел от конкретной реализации его валидатора. $validator = $this->get('validator'); $errors = $validator->validate($product);
  9. 9. OCP - Принцип открытости/закрытости Формулировка: • программные сущности (классы, модули, функции и т.д.) должны быть открыты для расширения, но закрыты для изменения Принцип открытости/закрытость дает понимание того, как оставаться достаточно гибкими в условиях постоянно меняющихся требований. Все классы, функции и т.д. должны проектироваться так, чтобы для изменения их поведения, нам не нужно было изменять их исходный код.
  10. 10. OCP - Принцип открытости/закрытости class Logger { public function Log($logText) { /* Save to file */ } } class SmtpMailer { private $logger; public function __construct() { $this->logger = new Logger(); } public function sendMessage($message) { // Send // Save to log $this->logger->Log($message); } }
  11. 11. OCP - Принцип открытости/закрытости class DbLogger { public function Log($logText) { /* Save to Db */ } } class SmtpMailer { private $logger; public function __construct() { $this->logger = new DbLogger(); } public function sendMessage($message) { // Send // Save to log $this->logger->Log($message); } }
  12. 12. OCP - Принцип открытости/закрытости interface LoggerInterface { public function Log($logText); } class Logger implements LoggerInterface { public function Log($logText) { /* Save to file */ } } class DbLogger implements LoggerInterface { public function Log($logText) { /* Save to file */ } }
  13. 13. OCP - Принцип открытости/закрытости class SmtpMailer { private $logger; public function __construct(LoggerInterface $logger) { $this->logger = $logger; } public function sendMessage($message) { // Send // Save to log $this->logger->Log($message); } }
  14. 14. OCP - Принцип открытости/закрытости Конкретизируя классы методом instanceof мы должны сразу понять, что наш код начал "попахивать": abstract class BaseEntity { } class AcountEntity extends BaseEntity { } class RoleEntity extends BaseEntity { } class Repository { public function save(BaseEntity $entiry) { if ($entiry instanceof AcountEntity) { // ... } else if ($entiry instanceof RoleEntity) { // ... } } }
  15. 15. OCP - Принцип открытости/закрытости 1. "Открыт для расширения": поведение может быть расширено путем добавления новых объектов, реализующих новые аспекты поведения; 2. "Закрыт для модификации": в результате расширения поведения исходный код объекта не может быть изменен.
  16. 16. LSP - Принцип замещения Лисков Формулировка №1: eсли для каждого объекта o1 типа S существует объект o2 типа T, который для всех программ P определен в терминах T, то поведение P не изменится, если o1 заменить на o2 при условии, что S является подтипом T.
  17. 17. LSP - Принцип замещения Лисков Формулировка №1: eсли для каждого объекта o1 типа S существует объект o2 типа T, который для всех программ P определен в терминах T, то поведение P не изменится, если o1 заменить на o2 при условии, что S является подтипом T. Формулировка №2: подтипы должны быть заменяемы базовыми типами.
  18. 18. LSP - Принцип замещения Лисков Поведение наследуемых классов не должно противоречить поведению, заданному базовым классом, то есть поведение наследуемых классов должно быть ожидаемым для кода, использующего переменную базового типа. interface CollectionInterface { public function get($index); public function count(); } class MyCollection implements CollectionInterface { public function get($index) { } public function count() {} } $myCollection = new MyCollection(); if (1 == $myCollection->count()) { $firstItem = $collection->get(0); // Exception, null }
  19. 19. LSP - Принцип замещения Лисков • Следовать этому принципу очень важно при проектировании новых типов с использованием наследования. • Этот принцип предупреждает разработчика о том, что изменение унаследованного производным типом поведения очень рискованно. Пример рассмотрен в книге Роберта Мартина «Быстрая разработка программ» в разделе «Принцип подстановки Лискоу. Реальный пример»
  20. 20. ISP - Принцип разделения интерфейса Формулировка: клиенты не должны зависеть от методов, которые они не используют Как и при использовании других принципов проектирования классов мы пытаемся избавиться от ненужных зависимостей в коде, сделать код легко читаемым и легко изменяемым.
  21. 21. ISP - Принцип разделения интерфейса interface IItem { public function applyDiscount($discount); public function applyPromocode($promocode); public function setColor($color); public function setSize($size); public function setCondition($condition); public function setPrice($price); }
  22. 22. ISP - Принцип разделения интерфейса interface IItem { public function setCondition($condition); public function setPrice($price); } interface IClothes { public function setColor($color); public function setSize($size); public function setMaterial($material); } interface IDiscountable { public function applyDiscount($discount); public function applyPromocode($promocode); }
  23. 23. ISP - Принцип разделения интерфейса class Book implemets IItem, IDiscountable { public function setCondition($condition){/*...*/} public function setPrice($price){/*...*/} public function applyDiscount($discount){/*...*/} public function applyPromocode($promocode){/*...*/} } class KidsClothes implemets IItem, IClothes { public function setCondition($condition){/*...*/} public function setPrice($price){/*...*/} public function setColor($color){/*...*/} public function setSize($size){/*...*/} public function setMaterial($material){/*...*/} }
  24. 24. DIP - Принцип инверсии зависимости Формулировка: • Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции. • Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
  25. 25. DIP - Принцип инверсии зависимости class OrderProcess { public function CalculeteTotal(Order $order) { $itemTotal = $order->getItemTotal(); $discountCalculator = new DiscountCalculator(); $discountAmount = $discountCalculator->calculateDiscount($order); $taxAmount = 0; if($order->getCountry() == "US") { $taxAmount = $this->getTaxAmount($order); } elseif ($order->getCountry() == "UK") { $taxAmount = $this->getVatAmount($order); } return $itemTotal - $discountAmount + $taxAmount; } private function getTaxAmount(Order $order) { } private function getVatAmount(Order $order) { } }
  26. 26. DIP - Принцип инверсии зависимости Причина, по которой проекты "стареют", заключается в том, что у разработчиков нет возможности безболезненно менять код каких-то компонентов без боязни нарушить работу других. Дизайн таких систем можно охарактеризовать следующими признаками: • Жесткость - изменение одной части кода затрагивает слишком много других частей; • Хрупкость - даже незначительное изменение в коде может привести к совершенно неожиданным проблемам; • Неподвижность - никакая из частей приложения не может быть легко выделена и повторно использована.
  27. 27. DIP - Принцип инверсии зависимости Перечислим все обязанности, которые выполняет класс OrderProcessor: • Знает, как вычислить сумму заказа; • Знает, как и каким калькулятором вычислить сумму скидки; • Знает, что означают коды стран; • Знает, каким образом вычислить сумму налога для той или иной страны; • Знает формулу, по которой из всех слагаемых вычисляется стоимость заказа. OrderProcess DiscountCalculator
  28. 28. DIP - Принцип инверсии зависимости interface DiscountCalculatorInterface { public function calculateDiscount(Order $order); } class DiscountCalculator implements DiscountCalculatorInterface { public function calculateDiscount(Order $order) { } } interface TaxStrategyInterface { public function getTaxAmount(Order $order); } class USTaxStarategy implements TaxStrategyInterface { public function getTaxAmount(Order $order) { } } class UKTaxStarategy implements TaxStrategyInterface { public function getTaxAmount(Order $order) { } }`
  29. 29. DIP - Принцип инверсии зависимости class OrderProcess { /** @var DiscountCalculatorInterface */ private $discountCalculator; /** @var TaxStrategyInterface */ private $taxStarategy; public function __construct(DiscountCalculatorInterface $discountCalculator, TaxStrategyInterface $taxStarategy) { $this->discountCalculator = $discountCalculator; $this->taxStarategy = $taxStarategy; } public function CalculeteTotal(Order $order) { $itemTotal = $order->getItemTotal(); $discountAmount = $this->discountCalculator->calculateDiscount($order); $taxAmount = $this->taxStarategy->getTaxAmount($order); return $itemTotal - $discountAmount + $taxAmount; } }
  30. 30. DIP - Принцип инверсии зависимости OrderProcess DiscountCalculator DiscountCalculatorInterface TaxStrategyInterface USTaxStarategy UKTaxStarategy
  31. 31. DIP - Принцип инверсии зависимости Принцип обращения зависимости - это очень мощный инструмент, который в сочетании с другими SOLID- принципами позволяет разрабатывать дизайн систем так же легко, как если бы он собирался из конструктора LEGO.
  32. 32. ?

×