Come capire a quale interfaccia eseguire il cast?

1

Questa domanda potrebbe essere un po 'soggettiva, ma ho provato tre diverse soluzioni e nessuno di loro ha avuto ragione. Fornirò un contesto e le soluzioni che ho provato. Il problema che sto affrontando sembra ridursi a se dovrei dividere un'interfaccia in più interfacce / sottointerfacce, o magari renderla generica. Tre diverse soluzioni (di cui sono a conoscenza)

Sto lavorando su un sistema di inventario per un gioco. Gli articoli, che possono essere inseriti nell'inventario, possono avere un'azione associata a loro che verrà eseguita quando si fa clic sull'elemento. Ciò si ottiene attraverso l'uso di un'interfaccia, IAction , che ha una dichiarazione del metodo void Perform(IActor actor) . Non tutti gli articoli implementano IAction , e diversi elementi possono avere azioni diverse da eseguire, facendo sì che l'interfaccia IActor contenga molte dichiarazioni di metodo.

La prima soluzione che ho usato aveva tutti questi metodi su IActor e non mi piaceva perché non tutti gli attori avevano bisogno di tutti i metodi su IActor . Ho deciso di creare sottointerfacce di IActor , come IHealthUser , come metodo per separare i metodi. Il motivo per cui ho mantenuto IActor come interfaccia di base è stato a causa della dichiarazione del metodo void Perform(IActor actor) su IAction . La soluzione è finita così:

public interface IActor { ... }
public interface IHealthUser : IActor { void AddHealth(int amount); }
public interface IAction { bool Perform(IActor actor); }
public class HealthPotion : IAction
{
    public bool Perform(IActor actor)
    {
        var performedAction = false;
        var healthUser = actor as IHealthUser;
        if (healthUser != null)
        {
            healthUser.AddHealth(Amount);
            performedAction = true;
        }
        return performedAction;
    }
}

e viene chiamato dal metodo del gestore eventi nell'inventario come questo

var action = Item as IAction;
if (action != null)
{
    var performedAction = action.Perform(Owner); // Where 'Owner' is IActor
    ...
}

Ho dovuto cambiare il tipo di ritorno di Perform() da void a bool per indicare se l'azione è stata effettivamente eseguita, dal momento che devo lanciare actor nel metodo prima di poter eseguire l'azione. Funziona, ma non sono sicuro che sia una buona soluzione "standard".

Ho anche provato a creare IAction generico:

public interface IAction<T> where T : IActor { void Perform(T actor); }

che rende l'implementazione di IAction<T> abbastanza pulita e elimina anche il valore di ritorno bool:

public class HealthPotion : IAction<IHealthUser>
{
    public void Perform(IHealthUser healthUser)
    {
        healthUser.AddHealth(Amount);
    }
}

Questa soluzione, tuttavia, mette un sacco di problemi per i chiamanti, che non ho una buona soluzione per:

// Do we have to cast and test for every possible IActor interface?
var action = Item as IAction<IHealthUser>;
var action = Item as IAction<IManaUser>;
...

La soluzione più semplice è lasciare che tutti i metodi rimangano in IActor , ma è brutto. La soluzione attuale, inserendo Perform() , funziona correttamente. La soluzione generica sembra la più giusta delle tre, se non fosse per il problema che ho avuto nel capire cosa lanciare Item prima di chiamare Perform() . La mia domanda, quindi, è, la soluzione generica è la strada da percorrere e, in caso affermativo, come dovrei andare a capire cosa lanciare Item in?

    
posta user1323245 24.01.2017 - 02:08
fonte

1 risposta

-2

Utilizza il modello dell'adattatore per isolare i client del visitatore da azioni non necessarie.

Implementa tutti i metodi di IActor in una classe di implementazione semplice, rendendo tutti i corpi del metodo vuoti. Fai in modo che ogni client crei un'istanza di IActor che ignori i metodi necessari per la classe di implementazione. Ciò renderà i clienti indipendenti dall'azione che non supportano e manterranno i vantaggi del Visitor classico. Non fare in modo che i clienti implementino lo stesso IActor, questo introdurrà dipendenze interne assicurative.

Quando la domanda suona "a cosa lancio" chiedo sempre "Perché ho bisogno di un cast, cosa è andato storto?" anziché. Cerca prima una soluzione espressa nel sistema di tipi, non lasciarla subito.

public interface IActor {
   void addHealth(int amount);
   void addMana(int amount);
}

public class ActorAdapter: IActor {
   void addHealth(int amount) {}
   void addMana(int amount) {}
}

class DullBeast {
   private int health = 40;
   public void getActor() {
      return new ActorAdapter() {
         void addHealth(int amount) {
            health += amount;
         }
         //I know nothing about magic and mana
      };
   }
}

class Magician {
   private int health = 60;
   private int mana = 10;
   public void getActor() {
      return new ActorAdapter() {
         void addHealth(int amount) {
            health += amount;
         }
         void addMana(int amount) {
            mana += amount;
         }
      };
   }
}
    
risposta data 24.01.2017 - 02:31
fonte

Leggi altre domande sui tag