2014-05-17 16 views
5

Pracuję nad prostym rozwiązaniem klient-serwer, w którym klient może wysyłać do serwera różne typy poleceń i uzyskiwać konkretne wyniki. Polecenia mogą mieć różne właściwości. Chciałbym mieć architekturę, w której można wybrać konkretną procedurę obsługi w zależności od typu komendy, którą obsługuje. Stworzyłem podstawową infrastrukturę w następujący sposób:Zastąpienie rzutowania lepszym wzorem

public interface ICommand 
{ 
} 

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

public class CommandB: ICommand 
{ 
} 

Każde polecenie ma własny CommandHandler, który jest odpowiedzialny za obsługę polecenia i zwracanie wyniku. Wszystkie one dziedziczą 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; 
    } 
} 

ja też stworzył klasę odpowiedzialną za wybór odpowiedniej obsługi i powrocie wynik:

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); 
    } 
} 

Nie lubię odlewania w klasie CommandHandlerBase, ale nie mogę znaleźć wszelkie wzorce, aby tego uniknąć. Mogę stworzyć ogólny interfejs, jak pokazano poniżej, ale jak zarejestrować się i wybrać konkretny program obsługi w ReplyCreator?

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

Komendy otrzymane w serwerze są szeregowane z Json.NET następująco:

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

Więc otrzymać ciąg, który w końcu trzeba być rozszeregować pod dowództwem betonu i obsługiwane za pomocą odpowiedniego uchwytu. Czy istnieje taki sposób unikania rzutów w takim scenariuszu? Używam StructureMap jako mojej biblioteki IoC.

Odpowiedz

3

Dlaczego próbujesz uniknąć tego rzutu? Wszelkie obejścia, które teraz przychodzą mi na myśl, nie będą ładniejsze niż to.

Po prostu chciałbym uniknąć użycia słowa kluczowego as w tym celu. Spowoduje to nieoczekiwane niepowodzenie w mało prawdopodobnym przypadku, gdy niewłaściwy typ zostanie przekazany do obsługi. W takich przypadkach, chcesz wyjątek być od razu, nie gdzieś później w kodzie.

+2

Uzgodnione. Zamiast 'Handle (polecenie jako TCommand)' powinno brzmieć polecenie 'Handle ((TCommand))'. – Timwi

+0

@SoftwareFactor Mogę na pewno żyć z castingiem, ale zdałem sobie sprawę, że ostatnio użyłem tego samego wzoru kilka razy i byłem ciekawy, czy można go zastąpić czymś innym, może lepszym. I jesteś grzeczny, nie powinienem używać "jak" w tym przypadku. –