Violato SRP per rifiutare il parametro del metodo sottoclasse

1

Stavo leggendo il blog di Eric Lippert su Wizard and Warriors .

Suggerisce la creazione di una classe Rules , citazione:

We keep on talking about “rules”, and so apparently the business domain of this program includes something called a “rule”, and those rules interact with all the other objects in the business domain. So then should “rule” be a class? I don’t see why not!

Le regole :

  • Un guerriero può usare solo una spada.
  • Una procedura guidata può utilizzare solo uno staff.

Forse non ci sto pensando nel modo giusto, ma suppongo di avere la seguente classe GameRules :

public final class GameRules {

    public static boolean verifyifwizardcancarry(Weapon weapon){
        boolean canCarry  = false
        if weapon is a a staff set canCarry to true
        return canCarry;
    }
}

e Player :

public abstract class Player{   

   private List<Weapon> weapons;    
   public abstract void add(Weapon weapon);

}


public final class Wizard extends Player{

   @Override 
   public void add(Weapon weapon){

      if(GameRules.verifyifwizardcancarry(weapon){
          // - code to add weapon to inventory
      }
    }
 }

Il rifiuto di una base di armi sul tipo (indipendentemente da dove è posizionata la logica) viola LSP ?

Nel mio metodo add(Weapon weapon) prometto che accetterò un'arma di qualsiasi tipo, quindi respingerla in base al tipo è una violazione di LSP, corretto? In tal caso, come imporrebbe le regole di cui sopra?

    
posta Deduplicator 05.06.2018 - 19:48
fonte

2 risposte

3

Se una sottoclasse viola la LSP, può essere verificata solo nel contesto del contratto di un metodo. Alcuni linguaggi di programmazione forniscono supporto esplicito per i costrutti, ma anche se non lo sono, si può sempre descrivere il contratto nella documentazione.

Quindi diciamo che il metodo astratto add assomiglia a questo:

 // - may or may not add weapon to the list "weapons"
 // - if adding is not possible, an exception can be thrown
 public abstract void add(Weapon weapon);

finché una sottoclasse implementa add obbedendo a queste regole, la loro non è violazione LSP. Tuttavia, se il contratto legge

 // - adds weapon to the list "weapons"
 // - no exception will be thrown
 public abstract void add(Weapon weapon);

l'implementazione nel tuo esempio violerà il LSP.

Nel caso in cui qualcuno non abbia specificato alcun comportamento, add non ha "proprietà provabili", quindi non c'è nulla da violare in termini di LSP, almeno non formalmente.

Tuttavia, la denominazione del metodo, i suoi parametri e la scelta del tipo di ritorno possono implicitamente portare a una certa aspettativa per l'utente di questa interfaccia, che può essere interpretata come una sorta di contratto "informale". Ad esempio, nominando il metodo TryAdd invece di solo add , sarebbe molto più chiaro che non garantisce l'aggiunta dell'arma all'elenco. Dandogli un tipo di ritorno booleano, questo probabilmente farebbe in modo che l'utente si aspetti che il metodo non generi un'eccezione nel caso in cui l'arma non venga aggiunta. Se una sottoclasse si comporta in modo diverso, questa può essere vista come una violazione "informale" dell'LSP o solo una violazione del POLA .

    
risposta data 05.06.2018 - 21:52
fonte
0

'Wizards and Warriors' è davvero un terribile esempio da scegliere. Apre il campo a tutti i tipi di soluzioni che non aiutano a spiegare il problema.

Preferisco di gran lunga Gatti e Cani

public abstract class Animal
{
    public abstract List<Animal> Children { get; set; }
}

public class Cat : Animal
{
    public override List<Cat> Children { get; set; }
}

public class Dog : Animal
{
    public override List<Dog> Children { get; set; }
}

Questo non funziona anche se sembra che dovrebbe. Se provi:

public class Cat : Animal
{
    public override List<Animal> Children { get; set; }
}

quindi puoi ottenere

Cat.Children.Add(new Dog);

e sei di nuovo nella sezione Wizards with Swords. Ma mentre è concepibile che un mago abbia una spada, nessuno penserà che un gatto possa avere cuccioli. Sarebbe pazzo (1)

La vera natura dell'eredità è di consentire la sostituzione che stiamo cercando di prevenire. Puoi lanciare eccezioni, o semplicemente non aggiungere ma poi interrompi il LSP

La soluzione (ben una delle soluzioni) è Generics

class Animal<T> where T : Animal<T>
{
    List<T> Childern;
}

class Cat : Animal<Cat> {}

Naturalmente alcune classi possono essere un asino e richiedono soluzioni più complesse.

(1) link

    
risposta data 05.06.2018 - 23:49
fonte

Leggi altre domande sui tag