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?