Programista PHP, Magento Developer
Aurora Creation sp. z o.o.
Łukasz Paliwoda
luk.paliwoda@gmail.com
l.paliwoda@auroracreation.com
S O L I D
“On the one hand, if the bricks aren’t well made, the architecture of the building
doesn’t matter much. On the other hand, you can make a substantial mess with
well-made bricks.”
Robert C. Martin
SOLID
SOLID is a acronym for five design principles intended to make software
designs more understandable, flexible and maintainable.
● Zebrane przez Robert C. Martin
● Nazwane przez Michael Feathers w 2004
S R P
O C P
L S P
I S P
D I P
Single Responsibility Principle
Open/Closed Principle
Liskov Substitution Principle
Interface Segregation Principle
Dependency Inversion Principle
S O L I D
SOLID
Single Responsibility Principle
“A class should only have a single responsibility, that is, only changes to one
part of the software's specification should be able to affect the specification of
the class.”
SOLID
SOLID
class Menu
{
public function getMenu()
{
$url = 'http://example.com/menu.xml';
$string = $this->download($url);
$response = $this->parse($string);
return $response;
}
private function parse($string) { /* some logic */ }
private function download($url) { /* some logic */ }
private function log($message) { /* some logic */ }
}
$menuDownloader = new Menu();
$menu = $menu->getMenu();
SOLID
class Menu
{
private $menuParser;
private $contentProvider;
public function __construct(
XmlParser $menuParser,
HttpClient $contentProvider
) {
$this->menuParser = $menuParser;
$this->contentProvider = $contentProvider;
}
public function getMenu()
{
$rawContent = $this->contentProvider->getRawContent();
$parsedMenu = $this->parser->parse($rawContent);
return $parsedMenu;
}
}
$parser = new XmlParser();
$httpClient = new HttpClient();
$menu = new Menu($parser, $httpClient);
try {
$menu = $menu->getMenu();
} catch (Exception $e) {
echo $e->getMessage();
}
S O L I D
SOLID
Open/Closed Principle
"Software entities ... should be open for extension, but closed for
modification."
SOLID
SOLID
class Publisher
{
public function display($type)
{
switch($type) {
case 'screen':
/* some logic */
break;
case 'rss':
/* some logic */
break;
}
}
}
$publsher = new Publisher();
$publsher->display('screen');
$publsher->display('rss');
SOLID
interface PublisherInterface
{
public function getContent();
}
class Publisher
{
public function display(PublisherInterface $publisher)
{
$publisher->getContent();
}
}
class ScreenPublisher implements PublisherInterface
{
public function getContent() { /* some logic */ }
}
class RssPublisher implements PublisherInterface
{
public function getContent() { /* some logic */ }
}
SOLID
$publsher = new Publisher();
$screenPublisher = new ScreenPublisher();
$publsher->display($screenPublisher);
$rssPublisher = new RssPublisher();
$publsher->display($rssPublisher);
class PrintPublisher implements PublisherInterface
{
public function getContent() { /* some logic */ }
}
$printPublisher = new PrintPublisher();
$publsher->display($printPublisher);
S O L I D
SOLID
Liskov Substitution Principle
"Objects in a program should be replaceable with instances of their subtypes
without altering the correctness of that program."
SOLID
SOLID
class Ptak
{
public function lec() { /* some logic */ }
}
class Golab extends Ptak
{
}
class Strus extends Ptak
{
public function lec()
{
throw new Exception('Not Implemented');
}
}
class Ptak
{
}
class LatajacyPtak extends Ptak
{
public function lec() { /* some logic */ }
}
public class Golab extends LatajacyPtak
{
}
public class Strus extends Ptak
{
}
SOLID
interface Jednoslad
{
public function jedz();
}
class Rower implements Jednoslad
{
public function jedz();
}
$notebook = new Rower();
$notebook->jedz();
class Motor implements Jednoslad
{
public function wlacz()
{
/* some logic */
}
public function jedz()
{
/* some logic */
}
}
$tablet = new Motor();
$tablet->jedz();
// throw new Exception("You need to start the engine")
S O L I D
SOLID
Interface Segregation Principle
"Many client-specific interfaces are better than one general-purpose
interface."
SOLID
SOLID
interface FormatterInterface
{
public function toPDF($content);
public function toXML($content);
public function toJSON($content);
}
class Formatter implements FormatterInterface
{
public function toPDF($content)
{
throw new Exception('Not Implemented');
}
public function toXML($content)
{
/* some logic */
}
public function toJSON($content)
{
/* some logic */
}
}
SOLID
interface PdfFormatterInterface
{
public function toPDF($content);
}
interface XmlFormatterInterface
{
public function toXML($content);
}
interface JsonFormatterInterface
{
public function toJSON($content);
}
class Formatter implements JsonFormatterInterface, XmlFormatterInterface
{
public function toJSON($content)
{
/* some logic */
}
public function toXML($content)
{
/* some logic */
}
}
S O L I D
SOLID
Dependency Inversion Principle
“Entities must depend on abstractions not on concretions.”
SOLID
SOLID
SOLID
class MysqlConnection
{
public function connect()
{
/* some logic */
}
}
class PasswordReminder
{
private $connection;
public function __construct(MysqlConnection $connection)
{
$this->connection = $connection;
}
}
SOLID
interface DbConnectionInterface
{
public function connect();
}
class MysqlConnection implements DbConnectionInterface
{
public function connect()
{
/* some logic */
}
}
class PasswordReminder
{
private $connection;
public function __construct(DbConnectionInterface $connection)
{
$this->connection = $connection;
}
}
Podsumowanie
● klasa powinna mieć jeden i tylko jeden powód do zmiany
● pozwól na zmianę zachowania systemu poprzez dodawanie nowego kodu,
a nie modyfikowanie istniejącego
● buduj elementy tak aby były wzajemnie wymienne
● unikaj tworzenia zależności od elementów których nie potrzebujesz
● uniezależnij od siebie kod implementujący poszczególne warstwy
DZIĘKUJĘ

SOLID

  • 1.
    Programista PHP, MagentoDeveloper Aurora Creation sp. z o.o. Łukasz Paliwoda luk.paliwoda@gmail.com l.paliwoda@auroracreation.com
  • 2.
    S O LI D
  • 3.
    “On the onehand, if the bricks aren’t well made, the architecture of the building doesn’t matter much. On the other hand, you can make a substantial mess with well-made bricks.” Robert C. Martin
  • 4.
    SOLID SOLID is aacronym for five design principles intended to make software designs more understandable, flexible and maintainable. ● Zebrane przez Robert C. Martin ● Nazwane przez Michael Feathers w 2004
  • 5.
    S R P OC P L S P I S P D I P Single Responsibility Principle Open/Closed Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle
  • 6.
    S O LI D
  • 7.
    SOLID Single Responsibility Principle “Aclass should only have a single responsibility, that is, only changes to one part of the software's specification should be able to affect the specification of the class.”
  • 8.
  • 9.
    SOLID class Menu { public functiongetMenu() { $url = 'http://example.com/menu.xml'; $string = $this->download($url); $response = $this->parse($string); return $response; } private function parse($string) { /* some logic */ } private function download($url) { /* some logic */ } private function log($message) { /* some logic */ } } $menuDownloader = new Menu(); $menu = $menu->getMenu();
  • 10.
    SOLID class Menu { private $menuParser; private$contentProvider; public function __construct( XmlParser $menuParser, HttpClient $contentProvider ) { $this->menuParser = $menuParser; $this->contentProvider = $contentProvider; } public function getMenu() { $rawContent = $this->contentProvider->getRawContent(); $parsedMenu = $this->parser->parse($rawContent); return $parsedMenu; } } $parser = new XmlParser(); $httpClient = new HttpClient(); $menu = new Menu($parser, $httpClient); try { $menu = $menu->getMenu(); } catch (Exception $e) { echo $e->getMessage(); }
  • 11.
    S O LI D
  • 12.
    SOLID Open/Closed Principle "Software entities... should be open for extension, but closed for modification."
  • 13.
  • 14.
    SOLID class Publisher { public functiondisplay($type) { switch($type) { case 'screen': /* some logic */ break; case 'rss': /* some logic */ break; } } } $publsher = new Publisher(); $publsher->display('screen'); $publsher->display('rss');
  • 15.
    SOLID interface PublisherInterface { public functiongetContent(); } class Publisher { public function display(PublisherInterface $publisher) { $publisher->getContent(); } } class ScreenPublisher implements PublisherInterface { public function getContent() { /* some logic */ } } class RssPublisher implements PublisherInterface { public function getContent() { /* some logic */ } }
  • 16.
    SOLID $publsher = newPublisher(); $screenPublisher = new ScreenPublisher(); $publsher->display($screenPublisher); $rssPublisher = new RssPublisher(); $publsher->display($rssPublisher); class PrintPublisher implements PublisherInterface { public function getContent() { /* some logic */ } } $printPublisher = new PrintPublisher(); $publsher->display($printPublisher);
  • 17.
    S O LI D
  • 18.
    SOLID Liskov Substitution Principle "Objectsin a program should be replaceable with instances of their subtypes without altering the correctness of that program."
  • 19.
  • 20.
    SOLID class Ptak { public functionlec() { /* some logic */ } } class Golab extends Ptak { } class Strus extends Ptak { public function lec() { throw new Exception('Not Implemented'); } } class Ptak { } class LatajacyPtak extends Ptak { public function lec() { /* some logic */ } } public class Golab extends LatajacyPtak { } public class Strus extends Ptak { }
  • 21.
    SOLID interface Jednoslad { public functionjedz(); } class Rower implements Jednoslad { public function jedz(); } $notebook = new Rower(); $notebook->jedz(); class Motor implements Jednoslad { public function wlacz() { /* some logic */ } public function jedz() { /* some logic */ } } $tablet = new Motor(); $tablet->jedz(); // throw new Exception("You need to start the engine")
  • 22.
    S O LI D
  • 23.
    SOLID Interface Segregation Principle "Manyclient-specific interfaces are better than one general-purpose interface."
  • 24.
  • 25.
    SOLID interface FormatterInterface { public functiontoPDF($content); public function toXML($content); public function toJSON($content); } class Formatter implements FormatterInterface { public function toPDF($content) { throw new Exception('Not Implemented'); } public function toXML($content) { /* some logic */ } public function toJSON($content) { /* some logic */ } }
  • 26.
    SOLID interface PdfFormatterInterface { public functiontoPDF($content); } interface XmlFormatterInterface { public function toXML($content); } interface JsonFormatterInterface { public function toJSON($content); } class Formatter implements JsonFormatterInterface, XmlFormatterInterface { public function toJSON($content) { /* some logic */ } public function toXML($content) { /* some logic */ } }
  • 27.
    S O LI D
  • 28.
    SOLID Dependency Inversion Principle “Entitiesmust depend on abstractions not on concretions.”
  • 29.
  • 30.
  • 31.
    SOLID class MysqlConnection { public functionconnect() { /* some logic */ } } class PasswordReminder { private $connection; public function __construct(MysqlConnection $connection) { $this->connection = $connection; } }
  • 32.
    SOLID interface DbConnectionInterface { public functionconnect(); } class MysqlConnection implements DbConnectionInterface { public function connect() { /* some logic */ } } class PasswordReminder { private $connection; public function __construct(DbConnectionInterface $connection) { $this->connection = $connection; } }
  • 33.
    Podsumowanie ● klasa powinnamieć jeden i tylko jeden powód do zmiany ● pozwól na zmianę zachowania systemu poprzez dodawanie nowego kodu, a nie modyfikowanie istniejącego ● buduj elementy tak aby były wzajemnie wymienne ● unikaj tworzenia zależności od elementów których nie potrzebujesz ● uniezależnij od siebie kod implementujący poszczególne warstwy
  • 34.

Editor's Notes

  • #2 programista PHP obecnie pracuję w firmie Aurora Creation sp. z o.o. zajmuję się pracą ze sklepami na platformie Magento tutaj są moje dane kontaktowe na wstępie chciałbym jeszcze dodać że nie mam doświadczenia w publicznych prezentacjach, dlatego proszę o wyrozumiałość oraz że będę posiłkował się notatkami
  • #3 kiedy szef poprosił aby powiedzieć kilka słów na PHPStock’u musiałem się zastanowić nad tematem o którym chciałbym powiedzieć jednym z popularnych tematów jest SOLID nie jest to temat nowy i na pewno wielu z was doskonale go zna Nie był on omawiany na PHPStock’u dlatego wypada nadrobić to niedopatrzenie Zaczynamy
  • #4 na początek cytat z jednej strony jeżeli cegiełki nie są solidnie zbudowane to architektura nie ma większego znaczenia. Z drugiej, można zrobić niezły bałagan z dobrze zbudowanych cegiełek to cytat z książki Roberta C. Martina (Uncle Bob) dość dobrze opisuje to z czym stykamy się na co dzień wiadomo że dobrą architekturę poznaje się nie po tym jak aplikacja działa w momencie puszczenia live a po tym jak znosi zmiany wymagań w trakcie wieloletniego procesu utrzymania czasem trzeba się dobrze nagłowić nad strukturą naszej aplikacji SOLID został stworzony aby pomóc nam tworzyć dobrą architekturę
  • #5 SOLID to akronim nazw 5 zasad traktujacych o dobrych praktykach tworzenia oprogramowania obiektowego Zostały zebrane przez Robert C. Martin pomiędzy końcówką lat ‘80 a początkiem lat 2000 nazwa SOLID została zaproponowana przez Michael’a Feather w mailu ok 2004 roku SOLID ma pomóc tworzyć kod który będzie dobrze znosił zmiany, będzie łatwiejszy w utrzymaniu i prostszy do zrozumienia
  • #6 SOLID to tak naprawdę trochę więcej liter jak wspomniałem to zbiór 5 zasad Nie są one czymś czego bezwzględnie trzeba przestrzegać, ale ich przestrzeganie pozwala budowa lepsze aplikacje teraz przejdziemy do omówienia poszczególnych reguł
  • #8 Każdy moduł/klasa powinny mieć jeden i tylko jeden powód do zmiany. Każda klasa powinna odpowiadać przed jednym aktorem systemu, tak aby zmiany wymagań jednego aktora nie wpływały na to jak działa aplikacja dla innych aktorów
  • #9 Załóżmy że mamy w firmie dział kadr, księgowości oraz techniczny. Programując system POWIĄZALIŚMY klasę Emploee z każdym z tych działów. Księgowość -> calculatepay Kadry -> reportHours Techniczny -> save W pewnym momencie księgowość uznała że trzeba zmienić sposób obliczania godzin Programista wprowadza zmiany w klasie regularHours Prowadzi to do niezamierzonej zmiany funkcjonowania metody reportHours
  • #10 Przykład kodu Klasa Menu Odpowiedzialna za pobranie i przeparsowanie zasobów z jakiegoś zewnętrznego źródła Na załączonym przykładzie klasa posiada metody Download - odpowiedzialną za pobranie danych Parse - odpowiedzialną za przeparsowanie pobranych danych Log - odpowiedzialną za zapisanie błędu Na tym przykładzie jedna klasa ma wiele odpowiedzialności W związku z tym zmiana dowolnej z tych funkcji może powodować błędy w pozostałych
  • #11 Na kolejnym przykładzie Poszczególne odpowiedzialności zostały wydzielone Do klas XmlParser oraz HttpClient Błędy natomiast są zwracane jako wyjątki Dzięki temu poszczególne funkcjonalności zostały rozdzielone I zmiana w klasie XmlParser nie wpłynie na działanie pozostałych elementów
  • #13 Otwarty na rozszerzenie / zamknięty na modyfikację Reguła rozsławiona przez Bertrand Meyer w latach ‘80 “Jeżeli oprogramowanie ma pozwalać na łatwe wprowadzanie zmian, to musi zostać zaprojektowane tak, żeby pozwalało na zmianę zachowania systemu poprzez dodawanie nowego kodu, a nie modyfikowanie istniejącego.”
  • #14 Załóżmy że piszemy klasę prezentującą jakąś treść, jakiś kontent Na początku ma ona być wyświetlane na ekranie Po pewnym czasie wymagania stawiane przed naszą klasą są rozszerzane i ma obsłużyć również drukarkę Dodajemy ify Po kolejnej zmianie wymagań klasa ma obsłużyć również kanał RSS Dodajemy ify Każda zmiana wymagała od nas powrotu do kodu i jego zmodyfikowania Uniknęlibyśmy tego gdybyśmy logikę odpowiedzialną za prezentacje danych wydzielili do oddzielnych klas A klasę menu zaprojektowali tak aby ich używała
  • #15 Przykład kodu Mamy klasę Publisher której zadaniem jest zaprezentowanie informacji w odpowiednim formacie Logika odpowiedzialna za sformatowanie danych została zawarta w prostym Switch’u Zmusza nas to do zmodyfikowania tego swicha za każdym razem gdy chcemy dodać nowy format
  • #16 Kolejny przykład definiuje interfejs Oraz zmusza klasę Publisher do pracy z interfejsem Logikę odpowiedzialną za formatowanie umieszczamy w klasach implementujących nasz interfejs
  • #17 Sposób w jaki mają zostać sformatowane dane przekazujemy z zewnątrz Dzięki temu dodanie nowego formatu ogranicza się do utworzenia klasy implementującej nasz interfejs W taki sposób uniknęliśmy modyfikowania istniejącego kodu przy dodawaniu nowych funkcji
  • #19 Powinna istnieć możliwość zastąpienia klas ich potomkami w taki sposób aby nie psuło to aplikacji Reguła liskov mówi nam o tym że elementy aplikacji powinny być na tyle wymienialne że możemy wziąć sobie jedną klasę, wstawić w to miejsce inną i wszystko powinno działać
  • #20 Załóżmy że do restauracji codziennie o 8 rano przywożą owoce, dzięki temu od tej godziny kucharz może przygotowywać dania gościom Któregoś dnia właściciel podpisał umowę z innym dostawcą, ale zapomniał powiedzieć mu że warzywa mają przyjechać o 8 Firma nie dostarczyła warzyw, kucharz nie miał jak przygotować potraw gościom Stało się tak bo został złamany kontrakt Gydby dostawa przyjechała na czas to wszystko działało by jak dawniej
  • #24 Unikaj tworzenia zależności od elementów których nie potrzebujesz Zgodnie z tą zasadą - tworzone interfejsy powinny zawierać minimalną liczbę deklaracji metod. Mniejsza ilość metod może być zrekompensowana możliwością dziedziczenia wielu interfejsów
  • #25 Najtrudniej było mi wymyślić przykład do tej zasady Każdy z nas ma klucze Ja swoje nosze w kieszeni
  • #26 Gdy interfejs ma wiele metod To implementując go musimy zaimplementować wszystkie jego metody Nie da się wydzielić kilku z nich Tak jak na przykładzie - musimy zaimplementować niewspieraną metodę toPDF
  • #27 Jeżeli natomiast mamy kilka interfejsów zawierających mniejszą liczbę metod To możemy dowolnie kształtować to co implementuje nasza klasa
  • #29 Klasy powinny zależeć od abstrakcji nie od konkretnych implementacji.
  • #30 Jeżeli Klasa 2 zależy od Klasy 1 to nie mamy możliwości zmienić jej implementacji Jeżeli natomiast Klasa 2 Będzie zależeć od interfejsu To możemy swobodnie podmienić Klasę 1 na inną klasę implementującą ten sam interfejs.
  • #31 nie mogłem się powstrzymać aby umieścić tu ten rysunek to ilustracja do wiersza Juliama Tuwima a nie do popularnego teleturnieju i z rzepki nie wystrzelą pieniążki Kto pamięta ten wiersz to wie że w ostatniej zwrotce wszystkie postacie leżały na ziemi Tak samo i my możemy leżeć jeżeli szef poprosi o wymianę klasy która leży w fundamentach systemu A która nie posiada abstrakcji
  • #32 Dla przykładu Klasa PasswordReminder zależy od MysqlConnection To znaczy że do konstruktora PasswordReminder zawsze musimy wrzucić MysqlConnection
  • #33 Jeżeli jednak utworzymy interfejs DbConnectionInterface I klasę PasswordReminder uzależnimy od tego interfejsu To do konstruktora będziemy mogli podać dowolną klasę implementującą ten interfejs. Innymi słowy “Nasza klasa klasę PasswordReminder będzie niezależna od konkretnej implementacji interfejsu DbConnectionInterface” Zasada odwracania zależności to zwieńćzenie całego SOLID. Zachęca do uzależniania się od abstrakcji, a nie od konkretnych implementacji rozwiązań.