Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SOLID principles

SOLID principles

Quick overview of SOLID principles.

Avatar for Przemysław Szpiler

Przemysław Szpiler

July 13, 2017

Other Decks in Programming

Transcript

  1. “Any fool can write a code that a computer can

    understand. Good programmers write code that humans can understand.” Martin Fowler
  2. Code smells ❏ Sztywność - żeby dodać ficzer trzeba zmienić

    “pół” platformy ❏ Kruchość - bardzo łatwo wprowadzić błędy ❏ Niemobilność - nie da się reużywać gotowych elementów ❏ Lepkość - zmiany “po bożemu” wymagają dużo zachodu
  3. SOLID SRP - Single Responsibility Principle OCP - Open Closed

    Principle LSP - Liskov Substitution Principle ISP - Interface Segregation Principle DIP - Dependency Inversion Principle
  4. Single Responsibility Principle zasada pojedynczej odpowiedzialności “Powód do modyfikacji klasy

    powinien być tylko jeden.” Klasa powinna koncentrować się na robieniu jednej rzeczy.
  5. Single Responsibility Principle class CalculateGradebookCommand { public function run($userId, $courseId)

    { $gradebook = $this->calculateGradebook($userId, $courseId); $this->saveGradebook($gradebook); } private function calculateGradebook($userId, $courseId) { // gradebook calculation } private function saveGradebook(Gradebook $gradebook) { // store gradebook in database } }
  6. Single Responsibility Principle Odpowiedzialność Odpowiedzialność Klasa niemobilność, kruchość, sztywność class

    CalculateGradebookCommand { public function run($userId, $courseId) { $gradebook = $this->calculateGradebook($userId, $courseId); $this->saveGradebook($gradebook); } private function calculateGradebook($userId, $courseId) { // gradebook calculation } private function saveGradebook(Gradebook $gradebook) { // store gradebook in database } }
  7. Single Responsibility Principle class CalculateGradebookCommand { public function run($userId, $courseId)

    { $gradebook = $this->calculateGradebook($userId, $courseId); $this->saveGradebook($gradebook); } private function calculateGradebook($userId, $courseId) { // gradebook calculation } private function saveGradebook(Gradebook $gradebook) { // store gradebook in database } } class GradebookCalculator { public function calculate($userId, $courseId) { // gradebook calculation } } class GradebookStorage { public function save(Gradebook $gradebook) { // store gradebook in database } }
  8. Single Responsibility Principle class GradebookCalculator { public function calculate($userId, $courseId)

    { // gradebook calculation } } class GradebookStorage { public function save(Gradebook $gradebook) { // store gradebook in database } } class CalculateGradebookCommand { private $gradebookCalculator; private $gradebookStorage; public function __construct( GradebookCalculator $gradebookCalculator, GradebookStorage $gradebookStorage ) { $this->gradebookCalculator = $gradebookCalculator; $this->gradebookStorage = $gradebookStorage; } public function run($userId, $courseId) { $gradebook = $this->gradebookCalculator ->calculate($userId, $courseId); $this->gradebookStorage->save($gradebook); } }
  9. Single Responsibility Principle ❏ Kod reużywalny ❏ Kod łatwiejszy w

    utrzymaniu ❏ Kod łatwiejszy do zrozumienia ❏ Kod łatwiejszy do testowania ❏ Bardziej trafne nazewnictwo ❏ Lepsze statyczne metryki kodu ❏ Eliminacja: niemobilności, kruchości, sztywności
  10. Open Closed Principle zasada otwarte / zamknięte “Encje systemu (klasy,

    moduły, metody, etc) powinny być otwarte na rozbudowę, ale zamknięte dla modyfikacji.”
  11. Open Closed Principle zasada otwarte / zamknięte “Encje systemu (klasy,

    moduły, metody, etc) powinny być otwarte na rozbudowę, ale zamknięte dla modyfikacji.” Zmieniaj zachowanie klasy przez kompozycje lub dziedziczenie.
  12. Open Closed Principle class GradebookExporter { public function export(Gradebook $gradebook)

    { $this->exportAsXml($gradebook); } private function exportAsXml(Gradebook $gradebook) { // exporting to xml logic } }
  13. Open Closed Principle class GradebookExporter { public function export(Gradebook $gradebook)

    { $this->exportAsXml($gradebook); $this->exportAsCsv($gradebook); } private function exportAsXml(Gradebook $gradebook) { // exporting to xml logic } private function exportAsCsv(Gradebook $gradebook) { // exporting to csv logic } }
  14. Open Closed Principle class GradebookExporter { public function export(Gradebook $gradebook)

    { $this->exportAsXml($gradebook); $this->exportAsCsv($gradebook); } private function exportAsXml(Gradebook $gradebook) { // exporting to xml logic } private function exportAsCsv(Gradebook $gradebook) { // exporting to csv logic } } kruchość, niemobilności, sztywność, lepkość
  15. Open Closed Principle class GradebookExporter { /** @var GradebookExporterInterface[] */

    private $exporters = []; public function addExporter(GradebookExporterInterface $e) { $this->exporters[] = $e; } public function export(Gradebook $gradebook) { foreach ($this->exporters as $exporter) { $exporter->export($gradebook); } } } rozbudowa przez kompozycje
  16. Open Closed Principle class GradebookExporter { /** @var GradebookExporterInterface[] */

    private $exporters = []; public function addExporter(GradebookExporterInterface $e) { $this->exporters[] = $e; } public function export(Gradebook $gradebook) { foreach ($this->exporters as $exporter) { $exporter->export($gradebook); } } } class XmlGradebookExporter implements GradebookExporterInterface { public function export(Gradebook $gradebook) { // exporting to xml logic } } interface GradebookExporterInterface { public function export(Gradebook $gradebook); } class CsvGradebookExporter implements GradebookExporterInterface { public function export(Gradebook $gradebook) { // exporting to csv logic } }
  17. Open Closed Principle class GradebookExporter { /** @var GradebookExporterInterface[] */

    private $exporters = []; public function addExporter(GradebookExporterInterface $e) { $this->exporters[] = $e; } public function export(Gradebook $gradebook) { foreach ($this->exporters as $exporter) { $exporter->export($gradebook); } } } services: gradebook.exporter: class: GradebookExporter calls: - [addExporter, ['xml.gradebook.exporter']] - [addExporter, ['csv.gradebook.exporter']] xml.gradebook.exporter: class: XmlGradebookExporter csv.gradebook.exporter: class: CsvGradebookExporter
  18. Open Closed Principle class GradebookExporter { /** @var GradebookExporterInterface[] */

    private $exporters = []; public function addExporter(GradebookExporterInterface $e) { $this->exporters[] = $e; } public function export(Gradebook $gradebook) { foreach ($this->exporters as $exporter) { $exporter->export($gradebook); } } } services: gradebook.exporter: class: GradebookExporter calls: - [addExporter, ['xml.gradebook.exporter']] - [addExporter, ['csv.gradebook.exporter']] - [addExporter, ['xls.gradebook.exporter']] xml.gradebook.exporter: class: XmlGradebookExporter csv.gradebook.exporter: class: CsvGradebookExporter xls.gradebook.exporter: class: XlsGradebookExporter
  19. Open Closed Principle class GradebookExporter { /** @var GradebookExporterInterface[] */

    private $exporters = []; public function addExporter(GradebookExporterInterface $e) { $this->exporters[] = $e; } public function export(Gradebook $gradebook) { foreach ($this->exporters as $exporter) { $exporter->export($gradebook); } } } services: gradebook.exporter: class: GradebookExporter calls: - [addExporter, ['xml.gradebook.exporter']] - [addExporter, ['csv.gradebook.exporter']] - [addExporter, ['xls.gradebook.exporter']] xml.gradebook.exporter: class: XmlGradebookExporter csv.gradebook.exporter: class: CsvGradebookExporter xls.gradebook.exporter: class: XlsGradebookExporter kod zamknięty dla modyfikacji kod otwarty na rozbudowę
  20. Open Closed Principle abstract class UsersNotifier { public function notifyUsers()

    { foreach ($this->getUsers() as $user) { $this->notify($user); } } abstract protected function getUsers(); // ... } class PrimaryStudentsNotifier extends UsersNotifier { protected function getUsers() { // get only primary students } } class AdultStudentsNotifier extends UsersNotifier { protected function getUsers() { // get only adult students } } rozbudowa przez dziedziczenie Metoda Szablonowa
  21. Open Closed Principle kod zamknięty dla modyfikacji kod otwarty na

    rozbudowę abstract class UsersNotifier { public function notifyUsers() { foreach ($this->getUsers() as $user) { $this->notify($user); } } abstract protected function getUsers(); // ... } services: primary.students.notifier: class: PrimaryStudentsNotifier adult.students.notifier: class: AdultStudentsNotifier teachers.notifier: class: TeachersNotifier
  22. Open Closed Principle ❏ Kod mniej podatny na błędy ❏

    Kod reużywalny ❏ Kod łatwiejszy w utrzymaniu ❏ Kod łatwiejszy do testowania ❏ Kod współgra z zasadą SRP ❏ Zachęca do stosowania wzorca Metoda Szablonowa ❏ Eliminacja: kruchości, niemobilności, sztywności, lepkości
  23. Liskov Substitution Principle zasada podstawienia Liskov “Musi być możliwość podstawienia

    typów pochodnych za ich typy bazowe.” Klasy pochodne powinny dobrze zachowywać się kiedy podstawimy je w miejsce klas bazowych.
  24. Liskov Substitution Principle ❏ Typowe naruszenia: ❏ klasa pochodna zmienia

    zachowanie klasy bazowej w sposób, którego nie spodziewa się klient ❏ klasa pochodna zgłasza wyjątki, których nie zgłasza klasa bazowa ❏ klasa pochodna realizuje mniej operacji niż klasa bazowa ❏ Naruszenie zasady LSP jest ukrytym naruszeniem zasady OCP
  25. Liskov Substitution Principle abstract class User { /** * @return

    int */ abstract public function getAge(); }
  26. Liskov Substitution Principle abstract class User { /** * @return

    int */ abstract public function getAge(); } public function calculateYearOfBirth(User $user) { return $this->getCurrentYear() - $user->getAge(); }
  27. Liskov Substitution Principle abstract class User { /** * @return

    int */ abstract public function getAge(); } class Student extends User { public function getAge() { return 20; } } public function calculateYearOfBirth(User $user) { return $this->getCurrentYear() - $user->getAge(); }
  28. Liskov Substitution Principle zgodnie z oczekiwaniem klienta - LSP spełnione

    abstract class User { /** * @return int */ abstract public function getAge(); } class Student extends User { public function getAge() { return 20; } } public function calculateYearOfBirth(User $user) { return $this->getCurrentYear() - $user->getAge(); }
  29. Liskov Substitution Principle abstract class User { /** * @return

    int */ abstract public function getAge(); } class Teacher extends User { public function getAge() { throw new \Exception('Top secret!'); } } class Student extends User { public function getAge() { return 20; } } public function calculateYearOfBirth(User $user) { return $this->getCurrentYear() - $user->getAge(); }
  30. Liskov Substitution Principle abstract class User { /** * @return

    int */ abstract public function getAge(); } class Teacher extends User { public function getAge() { throw new \Exception('Top secret!'); } } class Student extends User { public function getAge() { return 20; } } public function calculateYearOfBirth(User $user) { return $this->getCurrentYear() - $user->getAge(); } nie tego oczekiwał klient - LSP złamane kruchość
  31. Liskov Substitution Principle class Student extends User { public function

    save() { parent::save(); $this->log('student saved'); } } public function update(User $user) { $user->setName('test'); // ... $user->save(); } class Teacher extends User { public function save() { $this->log('teacher saved'); } } abstract class User { public function save() { $this->invalidateCache(); } // ... }
  32. Liskov Substitution Principle zgodnie z oczekiwaniem klienta - LSP spełnione

    class Student extends User { public function save() { parent::save(); $this->log('student saved'); } } public function update(User $user) { $user->setName('test'); // ... $user->save(); } class Teacher extends User { public function save() { $this->log('teacher saved'); } } abstract class User { public function save() { $this->invalidateCache(); } // ... } mniej operacji niż klasa bazowa - LSP złamane
  33. Liskov Substitution Principle ❏ Kod mniej podatny na błędy ❏

    Kod współgra z zasadą OCP ❏ Eliminacja: kruchości
  34. Interface Segregation Principle zasada segregacji interfejsów “Klienty nie powinny być

    zmuszone do zależności od metod, których nie używają.”
  35. Interface Segregation Principle zasada segregacji interfejsów “Klienty nie powinny być

    zmuszone do zależności od metod, których nie używają.” Interfejsy powinny być wąskie.
  36. Interface Segregation Principle interface Car { public function drive(); public

    function refuel(); public function charge(); } class ElectricCar implements Car { public function drive() { // implementation } public function refuel() { throw new NotImplementedMethod(); } public function charge() { // implementation } } class PetrolCar implements Car { public function drive() { // implementation } public function refuel() { // implementation } public function charge() { throw new NotImplementedMethod(); } }
  37. Interface Segregation Principle zależność od nieużywanych metod interface Car {

    public function drive(); public function refuel(); public function charge(); } class ElectricCar implements Car { public function drive() { // implementation } public function refuel() { throw new NotImplementedMethod(); } public function charge() { // implementation } } class PetrolCar implements Car { public function drive() { // implementation } public function refuel() { // implementation } public function charge() { throw new NotImplementedMethod(); } } sztywność, lepkość
  38. Interface Segregation Principle segregacja interfejsów interface Car { public function

    drive(); public function refuel(); public function charge(); } interface DriveableCar { public function drive(); } interface RefuelableCar { public function refuel(); } interface ChargeableCar { public function charge(); }
  39. Interface Segregation Principle interface DriveableCar { public function drive(); }

    interface RefuelableCar { public function refuel(); } interface ChargeableCar { public function charge(); }
  40. Interface Segregation Principle interface DriveableCar { public function drive(); }

    interface RefuelableCar { public function refuel(); } interface ChargeableCar { public function charge(); } class PetrolCar implements DriveableCar, RefuelableCar { public function drive() { // implementation } public function refuel() { // implementation } } class ElectricCar implements DriveableCar, ChargeableCar { public function drive() { // implementation } public function charge() { // implementation } }
  41. Interface Segregation Principle brak zależności od nieużywanych metod interface DriveableCar

    { public function drive(); } interface RefuelableCar { public function refuel(); } interface ChargeableCar { public function charge(); } class PetrolCar implements DriveableCar, RefuelableCar { public function drive() { // implementation } public function refuel() { // implementation } } class ElectricCar implements DriveableCar, ChargeableCar { public function drive() { // implementation } public function charge() { // implementation } }
  42. Interface Segregation Principle ❏ Mniej sprzężeń pomiędzy klasami, a interfejsami

    ❏ Kod współgra z zasadą SRP ❏ Eliminacja: sztywności, lepkości
  43. Dependency Inversion Principle zasada odwracania zależności “1) Moduły wysokopoziomowe nie

    powinny zależeć od modułów niskopoziomowych. I jedne i drugie powinny zależeć od abstrakcji. 2) Abstrakcje nie powinny zależeć od szczegółów. Szczegóły powinny zależeć od abstrakcji.”
  44. Dependency Inversion Principle zasada odwracania zależności “1) Moduły wysokopoziomowe nie

    powinny zależeć od modułów niskopoziomowych. I jedne i drugie powinny zależeć od abstrakcji. 2) Abstrakcje nie powinny zależeć od szczegółów. Szczegóły powinny zależeć od abstrakcji.” Twórz kod warstwowo. Separuj warstwy z użyciem interfejsów.
  45. Dependency Inversion Principle Moduł wysokopoziomowy (logika biznesowa) Moduł niskopoziomowy (szczegóły

    implementacji) Moduł wysokopoziomowy (logika biznesowa) Interfejs Moduł niskopoziomowy (szczegóły implementacji) Warstwa abstrakcji Warstwa implementacji
  46. Dependency Inversion Principle class UsersNotifier { /** @var EmailSender */

    private $emailSender; public function __construct(EmailSender $emailSender) { $this->emailSender = $emailSender; } public function notifyUsers(array $users, $message) { foreach ($users as $user) { $from = '[email protected]'; $to = $user->getEmail(); $this->emailSender->send($message, $from, $to); } } } class EmailSender { public function send($message, $from, $to) { // send email } } UsersNotifier (logika biznesowa) EmailSender (szczegóły implementacji)
  47. Dependency Inversion Principle class UsersNotifier { /** @var EmailSender */

    private $emailSender; public function __construct(EmailSender $emailSender) { $this->emailSender = $emailSender; } public function notifyUsers(array $users, $message) { foreach ($users as $user) { $from = '[email protected]'; $to = $user->getEmail(); $this->emailSender->send($message, $from, $to); } } } class EmailSender { public function send($message, $from, $to) { // send email } } UsersNotifier (logika biznesowa) EmailSender (szczegóły implementacji) sztywność, kruchość, niemobilność
  48. Dependency Inversion Principle class UsersNotifier { /** @var SenderInterface */

    private $sender; public function __construct(SenderInterface $sender) { $this->sender = $sender; } public function notifyUsers(array $users, $message) { foreach ($users as $user) { $this->sender->send($message, $user); } } }
  49. Dependency Inversion Principle class UsersNotifier { /** @var SenderInterface */

    private $sender; public function __construct(SenderInterface $sender) { $this->sender = $sender; } public function notifyUsers(array $users, $message) { foreach ($users as $user) { $this->sender->send($message, $user); } } } interface SenderInterface { public function send($message, User $user); }
  50. Dependency Inversion Principle class UsersNotifier { /** @var SenderInterface */

    private $sender; public function __construct(SenderInterface $sender) { $this->sender = $sender; } public function notifyUsers(array $users, $message) { foreach ($users as $user) { $this->sender->send($message, $user); } } } interface SenderInterface { public function send($message, User $user); } class EmailSender implements SenderInterface { public function send($message, User $user) { // send email to user } } class SmsSender implements SenderInterface { public function send($message, User $user) { // send sms to user } }
  51. Dependency Inversion Principle ❏ Jasny podział na warstwy (architektura heksagonalna)

    ❏ Kod reużywalny ❏ Kod mniej podatny na błędy ❏ Kod współgra z OCP ❏ Kod współgra z SRP ❏ Eliminacja: sztywności, kruchości, niemobilności
  52. QA