#4 SOLID – Interface segregation principle
W klasie implementującej interfejs znaleźć muszą się implementacje wszystkich metod zawartych w tym interfejsie. W przypadku gdy w danej klasie nie potrzebujemy wszystkich metod, pojawia się problem. Przestrzeganie kolejnej z zasad programowania obiektowego SOLID pozwoli nam uniknąć takich kłopotów.
Zasada Interface segregation principle mówi nam, że klasa nigdy nie powinna być zmuszana do implementacji metod, których nie używa. W celu osiągnięcia takiego stanu rzeczy należy tworzyć zwięzłe interfejsy zamiast tzw. grubych interfejsów. Powinny one zawierać metody ściśle ze sobą powiązane.
Przykład złamania
<?php /** * Interface ShapeInterface */ interface ShapeInterface { /** * @return float */ public function calcArea(): float; /** * @return float */ public function calcVolume(): float; }
<?php /** * Class Rectangle */ final class Rectangle implements ShapeInterface { /** * @var float */ private $width; /** * @var float */ private $height; /** * Rectangle constructor. * @param float $width * @param float $height */ public function __construct(float $width, float $height) { $this->width = $width; $this->height = $height; } /** * @return float */ public function getWidth(): float { return $this->width; } /** * @return float */ public function getHeight(): float { return $this->height; } /** * @return float */ public function calcArea(): float { return $this->width * $this->height; } /** * @return float */ public function calcVolume(): float { return 0; } }
<?php /** * Class Cuboid */ final class Cuboid implements ShapeInterface { /** * @var float */ private $a; /** * @var float */ private $b; /** * @var float */ private $c; /** * Cuboid constructor. * @param float $a * @param float $b * @param float $c */ public function __construct(float $a, float $b, float $c) { $this->a = $a; $this->b = $b; $this->c = $c; } /** * @return float */ public function getA(): float { return $this->a; } /** * @return float */ public function getB(): float { return $this->b; } /** * @return float */ public function getC(): float { return $this->c; } /** * @return float */ public function calcArea(): float { return 2 * ($this->a * $this->b + $this->c * $this->b + $this->a * $this->c); } /** * @return float */ public function calcVolume(): float { return $this->a * $this->b * $this->c; } }
W powyższym przykładzie mamy interfejs ShapeInterface oraz dwie klasy(Rectangle, Cuboid), które go implementują. Z racji, że w figurach płaskich obliczamy jedynie pole, natomiast objętość jest przeznaczona dla figur przestrzennych to metody calcArea() oraz calcVolume() nie powinny się znaleźć w jednym interfejsie.
Poprawny przykład
<?php /** * Interface AreaCalculableInterface */ interface AreaCalculableInterface { /** * @return float */ public function calcArea(): float; }
<?php /** * Interface VolumeCalculableInterface */ interface VolumeCalculableInterface { /** * @return float */ public function calcVolume(): float; }
<?php /** * Class Cuboid */ final class Cuboid implements VolumeCalculableInterface, AreaCalculableInterface { /** * @var float */ private $a; /** * @var float */ private $b; /** * @var float */ private $c; /** * Cuboid constructor. * @param float $a * @param float $b * @param float $c */ public function __construct(float $a, float $b, float $c) { $this->a = $a; $this->b = $b; $this->c = $c; } /** * @return float */ public function getA(): float { return $this->a; } /** * @return float */ public function getB(): float { return $this->b; } /** * @return float */ public function getC(): float { return $this->c; } /** * @return float */ public function calcArea(): float { return 2 * ($this->a * $this->b + $this->c * $this->b + $this->a * $this->c); } /** * @return float */ public function calcVolume(): float { return $this->a * $this->b * $this->c; } }
<?php /** * Class Rectangle */ final class Rectangle implements AreaCalculableInterface { /** * @var float */ private $width; /** * @var float */ private $height; /** * Rectangle constructor. * @param float $width * @param float $height */ public function __construct(float $width, float $height) { $this->width = $width; $this->height = $height; } /** * @return float */ public function getWidth(): float { return $this->width; } /** * @return float */ public function getHeight(): float { return $this->height; } /** * @return float */ public function calcArea(): float { return $this->width * $this->height; } }
W tym przykładzie interfejs został rozbity, przez co w klasie Rectangle implementujemy tylko AreaCalculableInterface i możemy usunąć zbędną metodę calcVolume(), dzięki czemu kod jest zgodny z zasadą ISP.
Pozostałe wpisy o SOLID
- SRP – Single responsibility principle (Zasada pojedynczej odpowiedzialności)
- OCP – Open/closed principle (Zasada otwarte/zamknięte)
- LSP – Liskov substitution principle (Zasada podstawienia Liskov)
- ISP – Interface segregation principle (Zasada segregacji interfejsów)
- DIP – Dependency inversion principle (Zasada odwrócenia zależności)
Subscribe and master unit testing with my FREE eBook (+60 pages)! 🚀
In these times, the benefits of writing unit tests are huge. I think that most of the recently started projects contain unit tests. In enterprise applications with a lot of business logic, unit tests are the most important tests, because they are fast and can us instantly assure that our implementation is correct. However, I often see a problem with good tests in projects, though these tests’ benefits are only huge when you have good unit tests. So in this ebook, I share many tips on what to do to write good unit tests.
Nie jest to tematem tego postu ale warto by poprawic wzor na pole prostopadloscianu, bo nie jest to raczej 2abc
Słusznie, nie wiem skąd to się wzięło, dzięki za czujność.