Note
By the time I wrote my answer, you had changed your example from an interface implementation to a base class inheritance. My answer is still correct; since the LSP applies to interfaces and base classes as per this StackOverflow answer.
You did change more in the example than just inheritance/interface, which does influence the answer. I've addressed this at the bottom of this answer.
Questa non è una violazione dell'LSP.
Sarebbe una violazione se il metodo è completamente inutilizzabile, cioè non dovresti mai chiamare il metodo TagTransformer.transform
.
Esempi della differenza.
Scusa la sintassi C #, è passato un po 'di tempo da quando eseguivo PHP.
public interface ICalculation
{
void SetFirstNumber(int num);
void SetSecondNumber(int num);
int CalculateOutputFromNumbers();
}
Implementazione 1:
public class Addition : ICalculation
{
public void SetFirstNumber(int num)
{
this.Number1 = num;
}
public void SetSecondNumber(int num)
{
this.Number2 = num;
}
public int CalculateOutputFromNumbers()
{
return this.Number1 + this.Number2;
}
}
Questo sta chiaramente utilizzando l'interfaccia come previsto. Nessun problema qui.
Implementazione 2:
public class SquareRoot: ICalculation
{
public void SetFirstNumber(int num)
{
this.Number1 = num;
}
public void SetSecondNumber(int num)
{
throw new Exception("Square roots only take one input value!);
}
public int CalculateOutputFromNumbers()
{
return Math.Sqrt(this.Number1);
}
}
Tieni in considerazione il modo in cui mai potrà chiamare il metodo SetSecondNumber
da SquareRoot
, anche se il metodo fa parte dell'interfaccia ICalculation
e SquareRoot
implementa% % co_de.
Ciò viola l'LSP. Per calcolare una radice quadrata, la classe ICalculation
deve essere trattata in modo diverso rispetto alle altre classi di implementazione di SquareRoot
.
Implementazione 3:
public class Division : ICalculation
{
public void SetFirstNumber(int num)
{
this.Number1 = num;
}
public void SetSecondNumber(int num)
{
if(num == 0)
throw new Exception("You can't divide by zero!");
this.Number2 = num;
}
public int CalculateOutputFromNumbers()
{
return this.Number1 / this.Number2;
}
}
In base alla tua domanda, sembra che ritieni che si tratti di una violazione di LSP. Questo è essenzialmente ciò che sta accadendo nel tuo esempio di codice, un'eccezione specifica viene lanciata per un dato valore non valido.
Questa non è una violazione. Osserva come è permesso chiamare il metodo ICalculation
da SetSecondNumber
, ma semplicemente non puoi usare un valore impossibile (0).
Non si tratta di dover utilizzare la classe Division
in modo diverso da Division
-le classi di implementazione; è semplicemente una questione di input errato, nessun output possibile .
Questo è significativamente diverso dall'esempio ICalculation
; almeno in relazione a LSP.
In risposta al tuo nuovo esempio
Il tuo nuovo esempio in un modo invalida la tua domanda originale.
Il tuo vecchio esempio era uno snippet PHP:
public function transform($origin)
{
if (!is_string($origin)) throw new InvalidArgumentException();
...
}
È importante notare che non esiste alcun vincolo di tipo su SquareRoot
. Ciò significa che il controllo di un tipo utilizzabile è una conseguenza logica e non un design intrinsecamente sbagliato (poiché il linguaggio consente parametri non tipizzati).
Tuttavia, questo è significativamente diverso nel tuo esempio rivisto:
public function eat(Food $food)
{
if ($food instanceof Meat) throw new InvalidArgumentException();
...
}
È importante notare che esiste un vincolo di tipo su $origin
.
Non stai più utilizzando un parametro senza tipo. Stai specificando che il parametro è di tipo $food
.
A questo punto, diventa una violazione LSP. La validazione dell'input non è più una questione di come funziona la lingua; ma piuttosto una conseguenza del contratto che è specificata dalla tua classe base Food
.
Stai provando a creare un Animal
che eredita da Cat
ma in realtà (parzialmente) completamente rifiuta di implementare Animal
. Questa è un'intenzionale esclusione della funzionalità, che ne fa una violazione LSP.
Prenderò in considerazione questa violazione di LSP di Eat(Food)
/ Meat
, più di quanto non sia una violazione LSP di Food
/ Cat
. La carne viene chiaramente trattata in modo diverso rispetto al cibo, il che viola il contratto secondo il quale la carne è un alimento.