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?