• Like
Принципы проектирования S.O.L.I.D
Upcoming SlideShare
Loading in...5
×

Принципы проектирования S.O.L.I.D

  • 10,861 views
Uploaded on

5 принципов проектировния программных продуктов

5 принципов проектировния программных продуктов

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
10,861
On Slideshare
0
From Embeds
0
Number of Embeds
8

Actions

Shares
Downloads
81
Comments
0
Likes
4

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. S.O.L.I.D Расшифруем: S - Single responsibility principle (SRP) O - Open/closed principle (OCP) L - Liskov substitution principle (LSP) I - Interface segregation principle (ISP) D - Dependency inversion principle (DIP)
  • 2. Зачем эти правила? Помогают построить архитектуру приложения, которое со временем возможно будет проще (дешевле) поддерживать и развивать. Помогают писать повторно используемый код.
  • 3. Принцип единственности ответственности (SRP) Не должно быть больше одной причины для изменения класса. Почему? Потому что это ведет к хрупкости дизайна (пишем один функционал - ”отваливается” другой).
  • 4. Высшая раса нарушающая SRP – это God Object Что такое ”плохо”:
  • 5. Борьба с высшей расой Лучше так:
  • 6. Принцип открытости/закрытости (OCP) Программные сущности (классы, модули, функции) должны быть открыты для расширения, но закрыты для изменения. Почему? Потому что это позволяет быстро и безболезненно реагировать на изменение бизнес-требований.
  • 7. class Logger { public function log($text) { // Сохраняем текст в лог (лог у нас будет храниться в файлах) } } class Product { private $_logger; public function __construct() { $this->_logger = new Logger(); } /* Продать товар */ public function sale() { // … продаем товар // Записываем дату продажи в лог $this->_logger->log('Sale time: '. time()); } } Чем плох этот код?
  • 8. Лог продаж в файлах?! Это же отстой! Изменение требований: лог надо хранить в БД class DBLogger { public function log($text) { // Сохраняем что-то в лог (лог у нас будет храниться в БД) } } class Product { private $_logger; public function __construct() { // Меняем класс Product, чтоб поменять логер (помните про SRP?) $this->_logger = new DBLogger(); } /* Продать товар*/ public function sale() { // Продаем товар // ... // Записываем дату продажи в лог $this->_logger->log('Sale time: '. time()); } }
  • 9. Готовимся к борьбе с изменениями требований (а не к борьбе с менеджерами) interface ILogger { public function log($text); } class Logger implements ILogger { public function log($text) { // Сохраняем что-то в лог (лог у нас будет храниться в файлах) } } class DBLogger implements ILogger { public function log($text) { // Сохраняем что-то в лог (лог у нас будет храниться в БД) } } class Product { private $_logger; public function __construct(ILogger $logger) { $this->_logger = $logger; } /* Продать товар */ public function sale() { // Продаем товар // ... // Записываем дату продажи в лог $this->_logger->log('Sale time: '. time()); } }
  • 10. Принцип подстановки Барбары Лисков (LSP) Поведение наследуемых классов не должно противоречить поведению, заданному базовым классом. Почему? Потому что клиентский код начинает считать производный класс разновидностью базового, и возможно появление кода, явно использующего этот факт.
  • 11. Байка про утку и батарейки
  • 12. Ударим кодом по уткам (базовый класс) /* Определим базовую утку */ abstract class Duck { private $_batteryStatus = 100; // Стстус заряда батареек утки (%) private $_steps = 0; // Количество шагов пройденных уткой (шт.) public function getBatteryStatus() { return $this->_batteryStatus; } public function setBatteryStatus($value) { $this->_batteryStatus = $value; } public function getSteps() { return $this->_steps; } public function setSteps($value) { $this->_steps = $value; } // Эти методы абстрактные (разные утки крякают и двигаются по-своему) abstract public function move(); abstract public function quack(); }
  • 13. Утки на батарейках (Америка, Китай) /* Американская утка на батарейках */ class AmericanDuck extends Duck { public function move() { $this->setSteps($this->getSteps() + 1); // Утка шагает $this->setBatteryStatus($this->getBatteryStatus() - 20); // Батарейки садятся echo "Я прошагала {$this->getSteps()} шагов (заряд батарейки: {$this->getBatteryStatus()}%) n"; } public function quack() { echo "Здравствуйте, я Американская утка! n"; } } /* Китайская утка на батарейках */ class ChinaDuck extends Duck { public function move() { $this->setSteps($this->getSteps() + 1); // Утка шагает $this->setBatteryStatus($this->getBatteryStatus() - 10); // Батарейки садятся (но не так быстро) echo "Я прошагала {$this->getSteps()} шагов (заряд батарейки: {$this->getBatteryStatus()}%) n"; } public function quack() { echo "Здравствуйте, я Китайский утка, я плохо говорить по-русски, но я уметь много шагать! n"; } }
  • 14. Клиентский код использования уток // Имеем список уток $duckList = array(new AmericanDuck(), new ChinaDuck()); // Просим всех уток по очереди представиться и походить foreach($duckList as $duck) { $duck->quack(); // Представимся while ($duck->getBatteryStatus() > 0) { // Утка должна шагать, пока не сядут батарейки $duck->move(); sleep(1); } echo "n"; }
  • 15. Появилась возожность юзать живую утку! /* Живая утка. Её особенность в том, что у нее нет батареек и она шагает сколько хочет */ class BrainyDuck extends Duck { public function move() { $this->setSteps($this->getSteps() + 1); // Утка шагает echo "Я прошагала {$this->getSteps()} шагов n"; } public function quack() { echo "Здравствуйте, я живая утка с мозгом! n"; } public function getBatteryStatus() { throw new Exception('Сума сошел? Какие батарейки, я живая!'); } public function setBatteryStatus() { throw new Exception('Сума сошел? Какие батарейки, я живая!'); } }
  • 16. Клиентский код и Fail исползования уток // Имеем список уток $duckList = array(new AmericanDuck(), new ChinaDuck(), new BrainyDuck()); // Просим всех уток по очереди представиться и походить foreach($duckList as $duck) { $duck->quack(); // Представимся while ($duck->getBatteryStatus() > 0) { // Утка должна шагать, пока не сядут батарейки $duck->move(); sleep(1); } echo "n"; }
  • 17. Принцип разделения интерфейса (ISP) Клиенты не должны зависеть от методов, которые они не используют. Почему? Если мы определим большой универсальный интерфейс, тогда в наследниках возможно появление множества заглушек, а соответственно, много лишнего кода, который неудобно поддерживать.
  • 18. Делаем трансформера (пока что все хорошо) interface IMegaTrsansformer { public function transformToCar(); public function transformToShip(); public function transformToPlane(); } class MegaTrsansformer implements IMegaTrsansformer { public function transformToCar() { echo 'Я преобразовался и стал спортивной машиной!'; } public function transformToShip() { echo 'Я преобразовался и стал сверхбыстрым катером!'; } public function transformToPlane() { echo 'Я преобразовался и стал истребителем!'; } }
  • 19. А теперь нужно сделать менее крутых трансформеров (в каждом из них теперь костыли) class TaxiTrsansformer implements IMegaTrsansformer { public function transformToCar() { echo 'Я преобразовался и стал такси!'; } public function transformToShip() { throw new Exception('Данная трансформация не поддерживается'); } public function transformToPlane() { throw new Exception('Данная трансформация не поддерживается'); } } class StelthTrsansformer implements IMegaTrsansformer { public function transformToCar() { throw new Exception('Данная трансформация не поддерживается'); } public function transformToShip() { throw new Exception('Данная трансформация не поддерживается'); } public function transformToPlane() { echo 'Я преобразовался и стал стелсом!'; } } class IcebreakerTrsansformer implements IMegaTrsansformer { public function transformToCar() { throw new Exception('Данная трансформация не поддерживается'); } public function transformToShip() { echo 'Я преобразовался и стал ледоколом!'; } public function transformToPlane() { throw new Exception('Данная трансформация не поддерживается'); } }
  • 20. Лучше иметь такой набор интерфейсов interface ICarTrsansformer { public function transformToCar(); } interface IShipTrsansformer { public function transformToShip(); } interface IPlaneTrsansformer { public function transformToPlane(); }
  • 21. И такой набор классов class MegaTrsansformer implements ICarTrsansformer, IShipTrsansformer, IPlaneTrsansformer { public function transformToCar() { echo 'Я преобразовался и стал спортивной машиной!'; } public function transformToShip() { echo 'Я преобразовался и стал сверхбыстрым катером!'; } public function transformToPlane() { echo 'Я преобразовался и стал истребителем!'; } } class TaxiTrsansformer implements ICarTrsansformer { public function transformToCar() { echo 'Я преобразовался и стал такси!'; } } class StelthTrsansformer implements IPlaneTrsansformer { public function transformToPlane() { echo 'Я преобразовался и стал стелсом!'; } } class IcebreakerTrsansformer implements IShipTrsansformer { public function transformToShip() { echo 'Я преобразовался и стал ледоколом!'; } }
  • 22. Принцип инверсии зависимостей (DIP)
    • Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций
    • 23. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций
  • 24. Анатомия зависимостей (X зависит от Y + циклическая зависимость) Зависимости бывают: - Прямые; - Транзитивные; - Циклические.
  • 25. Зависимость модуля верхннго уровня от модулей нижнего уровня class siteMapBilder { private $_dataStorage; private $_webGrabber; public function __construct() { $this->_dataStorage = new FileDataStorage(); $this->_webGrabber = new CurlWebGrabber(); } // ... Здесь какие-то методы для построения карты сайта }
  • 26. Освобождаем SiteMapBuilder от зависимостей (инвертируем зависимости) class siteMapBilder { private $_dataStorage; private $_webGrabber; public function __construct(WebGrabber $dataStorage, DataStorage $webGrabber) { $this->_dataStorage = $dataStorage; $this->_webGrabber = $webGrabber; } // ... Здесь какие-то методы для построения карты сайта }
  • 27. «... любые хорошо структурированные объектно-ориентированные архитектуры имеют четко определенные слои, каждый из которых поддерживает некоторый компактный набор служб с помошью хорошо определенного и контролируемого интерфейса» Г. Буч Разделение архитектуры по слоям
  • 28. Усовершенствованное разделение архитектуры по слоям Здесь мы избавились не только от транзитивной зависимости между TopLayer и DeepLayer, но и от прямых зависимостей
  • 29. Спасибо за внимание Вопросы?
  • 30. Почитать за чашкой кофе *
    • Приручите программные зависимости для большей гибкости приложений (Джеймс Ковач): http://msdn.microsoft.com/ru-ru/magazine/cc337885.aspx
    • 31. Принцип подстановки Барбары Лисков (Василий Меленчук): http://habrahabr.ru/blogs/programming/83269/
    • 32. Принципы проектирования классов (S.O.L.I.D.) (Александр Бындю): http://blog.byndyu.ru/2009/10/solid.html
    • 33. SRP (Robert C. Martin): www.objectmentor.com/resources/articles/srp.pdf
    • 34. OCP (Robert C. Martin): http://www.objectmentor.com/resources/articles/ocp.pdf
    • 35. LSP (Robert C. Martin): http://www.objectmentor.com/resources/articles/lsp.pdf
    • 36. ISP (Robert C. Martin): http://www.objectmentor.com/resources/articles/isp.pdf
    • 37. DIP (Robert C. Martin): http://www.objectmentor.com/resources/articles/dip.pdf
    • 38. * фоновая картинка здесь используется исключительно для красоты :-)