2017-04-10 2 views
2

Je travaille avec une application de bureau qui doit envoyer et recevoir des commandes via une liaison série avec un microprogramme programmé par mon collègue.Utilisation du modèle de commande pour envoyer et recevoir des commandes par série

Nous avons conçu un protocole où les types de commandes sont représentés par des lettres ASCII, chaque type de commande peut contenir une charge utile (arbitraire entre les types de commandes, mais fixée pour chaque type) et les commandes sont placées entre crochets. Par exemple, nous avons les commmands suivants:

  • -> Sent VROM le PC à l'appareil pour enregistrer un nouveau numéro de série ou de l'appareil à l'ordinateur pour informer le numéro de série en cours (sorte de getter/commande de setter);
  • [R] - Envoyé du PC au périphérique pour demander une commande "get serial";
  • [A12] - Envoyé de l'appareil au PC pour informer une nouvelle lecture ADC;
  • [B] - Envoyé du PC au périphérique pour demander une charge de la batterie;
  • `[B89] - Envoyé de l'appareil au PC pour informer la charge de la batterie;
  • Etc

J'ai donc une classe qui reçoit et analyse les octets entrants, et chaque fois qu'une commande est analysée avec succès, un événement est soulevé, avec la signature provisoire suivant:

internal event EventHandler<SerialCommand> CommandReceived; 

SerialCommand aurait différents sous-types: BatteryCommand, AdcCommand, SerialCommand et autres. Chaque type de commande doit être associé à son "code char" respectif.

Ma question est: comment le code client devrait-il utiliser cela? Par exemple, la mise en œuvre en cours lorsque je reçois une commande dispose d'un commutateur/cas littéraux char codées en dur, que je trouve très fragile et laid:

void CommandReceivedHandler(object sender, SerialCommand command) 
    { 
     switch (command.Code) 
     { 
      case 'A': 
       int value= Convert.ToInt32(command.Value); 
       _frameStreamer.AddFrame(new Frame<int>(new[] { value})); 
       break; 
      case 'B': 
       BatteryLevel= (int)command.Value; 
       break; 
      case 'D': 
       DoSomething((byte)command.Value); 
       break; 
      case 'S': 
       SerialNumber = (int)command.Value; 
       break; 
     }   
    } 

Actuellement, ces « codes char » sont répartis autour d'une tas de cours, et si jamais j'ai besoin de changer un code donné, j'aurais besoin de regarder autour de moi pour chaque occurrence (fusil de chasse anti-pattern).

Ce que je dois faire sont deux choses:

  • codes Encapsulate char dans les commandes très seulement, au lieu de code client;
  • Exécuter de façon polymorphique des actions sur le client (consommateur d'événements CommandReceived) de préférence sans l'instruction switch/case.
+0

carte peut-être simple peut vous aider, vérifier la dernière réponse à ce poste, http://stackoverflow.com/questions/43035984/what-design- pattern-is-proper-for-this-situation – dstar55

Répondre

2

Vous pouvez essayer quelque chose comme ceci:

public abstract class BaseCommand 
{ 
    //Code not needed at all, because logic encapsulated into command 
    //public char Code { get; set; }   
    public abstract void Action(IClient client); 
} 

public abstract class BaseCommand<T> : BaseCommand 
{ 
    public T value { get; set; } 
} 

public class CommandA : BaseCommand<int> 
{    
    public override void Action(IClient client) 
    {   
     client.someInt = value * 2; 
    } 
} 

public class CommandB : BaseCommand<string> 
{ 
    public override void Action(IClient client) 
    { 
     client.someString = value.Trim(); 
    } 
} 

public interface IClient 
{ 
    void CommandReceivedHandler(object sender, BaseCommand command);  
    int someInt { get; set; } 
    string someString { get; set; } 
} 

public class Client : IClient 
{ 
    public void CommandReceivedHandler(object sender, BaseCommand command) 
    { 
     command.Action(this); 
    } 

    public int someInt { get; set; } 
    public string someString { get; set; } 
} 
+0

J'ai aimé la façon dont vous fournissez l'instance du client à l'instance de commande, au lieu du contraire que j'ai essayé. Je crois que c'est la façon dont le modèle de commande est destiné à être utilisé - soit en passant une instance de client ou une métode client à la commande. – heltonbiker

+0

En fait, je pense que je vais aller avec une variante où je passe le client via le constructeur, pas via l'appel d'action. De toute façon, les collaborations sont équivalentes. Merci! – heltonbiker