2014-05-17 7 views
5

Sto lavorando su una semplice soluzione client-server in cui il client può inviare diversi tipi di comandi al server e ottenere risultati specifici. I comandi possono avere proprietà diverse. Quello che mi piacerebbe avere è un'architettura in cui un gestore di comandi specifico potrebbe essere scelto in base al tipo di comando che gestisce. Ho creato infrastrutture di base come segue:Sostituire la fusione con un modello migliore

public interface ICommand 
{ 
} 

public class CommandA: ICommand 
{ 
    public string CustomProperty { get; set; } 
} 

public class CommandB: ICommand 
{ 
} 

Ogni comando ha un proprio CommandHandler, che è responsabile della gestione del comando e il risultato di ritorno. Tutti ereditano da CommandHandlerBaseClass:

public interface ICommandHandler 
{ 
    bool CanHandle(ICommand command); 
    IReply Handle(ICommand command); 
} 

public abstract class CommandHandlerBase<TCommand> : ICommandHandler 
    where TCommand : class, ICommand 
{ 
    public bool CanHandle(ICommand command) 
    { 
     return command is TCommand; 
    } 

    public IReply Handle(ICommand command) 
    { 
     return Handle(command as TCommand); 
    } 

    public abstract IReply Handle(TCommand command); 
} 

// Specific handler 
public class CommandAHandler : CommandHandlerBase<CommandA> 
{ 
    public override IReply Handle(CommandA command) 
    { 
     //handling command and returning result 
     return null; 
    } 
} 

Ho anche creato una classe responsabile della scelta del gestore adatto e restituendo risultato:

public interface IReplyCreator 
{ 
    IReply GetReply(ICommand command); 
} 

public class ReplyCreator : IReplyCreator 
{ 
    private readonly IEnumerable<ICommandHandler> _commandHandlers; 

    public ReplyCreator(IEnumerable<ICommandHandler> commandHandlers) 
    { 
     _commandHandlers = commandHandlers; 
    } 

    public IReply GetReply(ICommand command) 
    { 
     var commandHandler = _commandHandlers 
      .FirstOrDefault(x => x.CanHandle(command)); 

     if (commandHandler == null) 
      return null; 
     return commandHandler.Handle(command); 
    } 
} 

Non mi piace la fusione in classe CommandHandlerBase, ma non riesco a trovare qualsiasi modello per evitarlo. Potrei creare un'interfaccia generica come mostrato di seguito, ma come registrare e scegliere un gestore specifico in ReplyCreator allora?

public interface ICommandHandler<TCommand> 
    where TCommand : ICommand 
{ 
    bool CanHandle(TCommand command); 
    IReply Handle(TCommand command); 
} 

I comandi ricevuti nel server vengono serializzati con Json.net come segue:

JsonConvert.SerializeObject(new CommandA(), new JsonSerializerSettings 
    { 
     TypeNameHandling = TypeNameHandling.All 
    };) 

Così ho ricevere una stringa che alla fine devono essere deserializzato in comando cemento e gestito da adatto gestore. C'è un modo per evitare il cast in un simile scenario? Uso StructureMap come libreria IoC.

risposta

3

Perché stai cercando di evitare questo cast? Qualsiasi soluzione alternativa che mi viene in mente ora, non sarà più bella di questa.

Eviterei semplicemente di utilizzare la parola chiave as per questo scopo. Fallirà silenziosamente nel caso improbabile quando il tipo sbagliato viene passato al gestore. In casi come questo, vuoi che l'eccezione sia lanciata immediatamente, non da qualche parte più avanti nel tuo codice.

+2

concordato. Invece di 'Handle (comando come TCommand)', dovrebbe dire 'Handle (comando (TCommand))'. – Timwi

+0

@SoftwareFactor Posso sicuramente vivere con il casting, ma mi sono reso conto che ho usato pattern simili più volte di recente ed ero curioso di poterlo sostituire con qualcos'altro, forse meglio. E tu sei severo, non dovrei usare "come" in questo caso. –

Problemi correlati