“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
{ $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 } }
utrzymaniu ❏ Kod łatwiejszy do zrozumienia ❏ Kod łatwiejszy do testowania ❏ Bardziej trafne nazewnictwo ❏ Lepsze statyczne metryki kodu ❏ Eliminacja: niemobilności, kruchości, sztywności
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
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 } }
{ 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
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
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
int */ abstract public function getAge(); } class Student extends User { public function getAge() { return 20; } } public function calculateYearOfBirth(User $user) { return $this->getCurrentYear() - $user->getAge(); }
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(); }
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(); }
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ść
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(); } // ... }
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
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(); } }
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ść
drive(); public function refuel(); public function charge(); } interface DriveableCar { public function drive(); } interface RefuelableCar { public function refuel(); } interface ChargeableCar { public function charge(); }
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 } }
{ 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 } }
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.”
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.
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); }
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 } }