Volevo solo verificare di aver compreso correttamente l'LSP e di poterlo risolvere. Sto prendendo il classico problema rettangolo / quadrato e tentando una soluzione:
class Rectangle{
public $width;
public $height;
function setWidth($width){
$this->width = $width;
}
function setHeight($height){
$this->height = $height;
}
}
class Square extends Rectangle{
function setWidth($width){
$this->width = $width;
$this->height = $width;
}
function setHeight($height){
$this->height = $height;
$this->width = $height;
}
}
Se hai un codice come:
function changeSize(Rectangle $rect){
$rect->setWidth(10);
$rect->setHeight(30);
$this->assertEquals(10,$rect->width);
$this->assertEquals(30,$rect->height);
}
Quindi ovviamente i rettangoli e i quadrati non sono intercambiabili, poiché piazza introduce un vincolo alla classe genitore. Pertanto, un quadrato non dovrebbe ereditare dai rettangoli.
Ma sicuramente possiamo essere d'accordo sul fatto che sia il quadrato sia il rettangolo sono forme a quattro lati? Questa è la mia soluzione proposta, basata su questa premessa:
abstract class AFourSidedShape{
public $width;
public $height;
abstract public function __construct($width,$height);
public function scaleUp($percentage){
$this->height = $this->height + (($this->height / 100) * $percentage);
$this->width = $this->width + (($this->width / 100) * $percentage);
}
public function scaleDown($percentage){
$this->height = $this->height - (($this->height / 100) * $percentage);
$this->width = $this->width - (($this->width / 100) * $percentage);
}
}
class Rectangle extends AFourSidedShape{
function __construct($width, $height){
$this->width = $width;
$this->height = $height;
}
}
class Square extends AFourSidedShape{
function __construct($width, $height){
if($width != $height){
throw new InvalidArgumentException('Sides must be equal');
}else{
$this->width = $width;
$this->height = $height;
}
}
}
Il nostro codice cliente dovrebbe essere cambiato in qualcosa tipo:
function changeSize(AFourSidedShape $shape){
$origWidth = $shape->width;
$origHeight = $shape->height;
$shape->scaleUp(10);
$this->assertEquals($origWidth + (($origWidth/100) * 10),$shape->width);
$this->assertEquals($origHeight + (($origHeight/100) * 10),$shape->height);
}
La mia teoria è: rettangoli e quadrati sono in realtà entrambi formati da un lato, quindi non dovrebbe esserci un problema con l'ereditare dalla classe astratta foursidedshape. Mentre il quadrato sta ancora aggiungendo vincoli extra nel costruttore (cioè lanciare un errore se i lati non sono uguali), non dovrebbe essere un problema poiché non abbiamo implementato il costruttore nella classe genitore astratta, e quindi il codice cliente non dovrei fare supposizioni su ciò che puoi / non puoi passare comunque.
La mia domanda è: ho capito LSP, e questo nuovo design risolve il problema LSP per quadrato / rettangolo?
Quando si usano le interfacce come suggerito:
interface AFourSidedShape{
public function setWidth($width);
public function setHeight($height);
public function getWidth();
public function getHeight();
}
class Rectangle implements AFourSidedShape{
private $width;
private $height;
public function __construct($width,$height){
$this->width = $width;
$this->height = $height;
}
public function setWidth($width){
$this->width = $width;
}
public function setHeight($height){
$this->height = $height;
}
//getwidth, getheight
}
class Square implements AFourSidedShape{
private $width;
private $height;
public function __construct($sideLength){
$this->width = $sideLength;
$this->height = $sideLength;
}
public function setWidth($width){
$this->width = $width;
$this->height = $width;
}
public function setHeight($height){
$this->height = $height;
$this->width = $height;
}
//getwidth, getheight
}