SOLID
Piotr Woszczyk @ 2020
Czym jest SOLID?
- Akronim
- Zbiór zasad programowania obiektowego
- Opracowany przez Roberta Martina w 2000 roku
- Możliwy do użycia w większości współczesnych języków programowania
- Niezależny od frameworka, środowiska, etc.
Single responsibility
- Klasa / metoda powinna być odpowiedzialna za wykonanie tylko jednej czynności
- Nie powinien istnieć więcej jak jeden powód do zmiany klasy / metody
- Zyskujemy czytelniejszy kod oraz łatwiejszy refactoring
<?php
class Person {
public $name;
public function render() {
echo '<p>'.$this->name.'</p>';
}
public function validate() : bool {
return null == $this->name;
}
}Przykład negatywny
<?php
class Person {
public $name;
}
class Validator {
protected $person;
public function __construct(Person $person) {
$this->person = $person;
}
public function validate() : bool {
return null == $this->person->name;
}
}
class Template {
protected $person;
public function __construct(Person $person) {
$this->person = $person;
}
public function render() {
echo '<p>'.$this->person->name.'</p>';
}
}Przykład pozytywny
Open–closed
- Kod powinien być gotowy na rozszerzenie i zamknięty na modyfikacje
- Nowe funkcjonalności powinny być możliwe do dodania bez konieczności zmiany istniejącego kodu
- Zyskujemy wyższą kompatybilność wsteczną i mniejsze ryzyko uszkodzenia dotychczasowego kodu
<?php
class File {
public $extension;
}
class Importer {
public function execute($file) {
if ($file->extension == 'xls') {
$this->importXls($file);
} else if ($file->extension == 'xml') {
$this->importXml($file);
}
}
}Przykład negatywny
<?php
class File {
public $extension;
}
interface Formater() {
public function supports(string $format) : bool;
public function format(File $file) : array;
}
class Importer {
protected $formaters;
public function registerFormaters(FormaterInterface $formater) {
$this->formaters[] = $formater;
}
public function execute($file) {
foreach ($this->formaters as $formater) {
if ($formater->supports($file->extension)) {
$data = $formeater->format($file);
break;
}
}
// ...
}
}Przykład pozytywny
Liskov substitution
- Klasy potomne powinny rozszerzać możliwości klasy rodzica zachowując działanie klasy rodzica
- Zyskujemy brak niespodziewanych zachowań w trakcie wykonywania kodu
?<php
class CRUD {
public function create() {}
public function read() {}
public function update() {}
public function delete() {}
}
class ReadonlyUserCRUD extends CRUD {
public function create() {
throw new Exception();
}
public function update() {
throw new Exception()
}
public function delete() {
throw new Exception()
}
}Przykład negatywny
?<php
class Read {
public function execute() {}
}
class ReadonlyUserCRUD extends Read {
protected $logger;
public function execute() {
$this->logger->debug('ReadonlyUserCRUD::execute');
return parent::execute();
}
}Przykład pozytywny
Interface segregation
- Interfejsy powinny definiować możliwie mały zestaw metod
- Nie należy zbytnio uogólniać interfejsów
- Zyskujemy łatwiejsze implementowanie interfejsów oraz nie powstaje nadmiarowy kod
<?php
interface Exportable {
public function toPDF() {};
public function toHTML() {};
}
class ReportExport implements Exportable {
public function toPDF() {}
public function toHTML() {}
}
class ImageExport implements Exportable {
public function toPDF() {
return;
}
public function toHTML() {}
}Przykład negatywny
<?php
interface ExportableToPDF {
public function toPDF() {};
}
interface ExportableToHTML {
public function toHTML() {};
}
class ReportExport implements ExportableToPDF, ExportableToHTML {
public function toPDF() {}
public function toHTML() {}
}
class ImageExport implements ExportableToHTML {
public function toHTML() {}
}Przykład pozytywny
Dependency inversion
- Klasy wysokiego poziomu (np. realizujące logikę biznesową) nie powinny być zależne od klas niskiego poziomu (np. dostarczających dane)
- Zależności między klasami powinny być definiowane przez dodatkową warstwę abstrakcji (zazwyczaj interfejsy)
- Zyskujemy zmniejszenie siły zależności między klasami oraz możliwość późniejszej ich wymiany
Przykład negatywny
<?php
class Parser {
public function getHeaders() {}
}
class Importer {
public function execute() {
$parser = new Parser();
$headers = $parser->getHeaders();
// ...
}
}<?php
interface RequestInterface() {
public function getHeaders();
}
class Parser implements RequestInterface {
public function getHeaders() {}
}
class Importer {
protected $parser;
public function __construct(RequestInterface $parser) {
$this->parser = $parser;
}
public function execute() {
$headers = $this->parser->getHeaders();
// ...
}
}Przykład pozytywny

SOLID
By Piotr Woszczyk
SOLID
- 48