2017-05-16 1 views
0

J'ai développé un protocole de communication personnalisé. Dans ce protocole, chaque paquet est constitué d'un en-tête et d'une charge utile. Chaque charge contient plusieurs commandes. Je voudrais implémenter une méthode 'décode' statique dans chaque commande (Command0, ..., Commande N) car toutes les commandes du même type sont décodées de la même manière (elles ont la même structure de champ). J'utilise une classe de classe 'Command' comme modèle avec quelques champs et méthodes communs et une méthode de 'décodage' abstraite pour mes commandes. Mais puisque la méthode de la superclasse est abstraite, la méthode 'decode' dans Command0, ..., CommandN ne peut pas être statique.Méthode abstraite d'une superclasse abstraite en tant que méthode statique dans la sous-classe

Des solutions de rechange? Je voudrais éviter d'instancier chaque commande chaque fois que je dois décoder un paquet. Comme alternative, je me suis débarrassé de la méthode 'decode' abstraite et j'ai inclus une méthode 'decode' statique dans chaque commande d'extension de commande. Cependant, sans une superclasse ou une interface, un autre programmeur pourrait oublier d'implémenter une méthode de décodage. Cette alternative conduirait au code suivant:

{ 
    switch(commandIdentifier) 
    { 
     case 0: 
     { 
      Command0 command0 = Command0.decode(dbConnection, header, data, offset); 
      payload.getCommands().add(command0); 
      break; 
     } 
     //... 
     case N: 
     { 
      CommandN commandN = CommandN.decode(dbConnection, header, data, offset); 
      payload.getCommands().add(commandN); 
      break; 
     } 
     default: 
     { 
      //some code 
     } 
    } 
} 

où je dois d'abord pour vérifier l'identificateur de commande.

J'ai d'abord mis en œuvre les classes de cette façon:

public class Packet 
{ 
    private Header header; 
    private Payload payload; 

    public static Packet decode(Connection dbConnection, byte[] data, int offset) throws Exception 
    { 
     //... 
    } 
} 

public class Header 
{ 
    public static Header decode(byte[] data, int offset) 
    { 
     //... 
    } 
} 

public class Payload 
{ 
    private List<Command> commands; 
    public static Payload decode(Connection dbConnection, Header header, byte[] data, int offset) 
    { 
     //iterate over the data bytes to populate commands 
    } 
} 

public abstract class Command 
{ 
    public abstract Command decode(Connection dbConnection, Header header, byte[] data, int offset) throws Exception; 
} 

public class Command0 
{ 
    int field1; 
    String field2; 
    float field3; 
    public Command decode(Connection dbConnection, Header header, byte[] data, int offset) throws Exception; //I can't make it static and I'd like to because all commands of class Command0 are decoded the same way. 
} 

//... 

public class Command N 
{ 
    int field1; 
    Map<Integer, ConfigBean> field2; 
    public Command decode(Connection dbConnection, Header header, byte[] data, int offset) throws Exception; //I can't make it static and I'd like to because all commands of class CommandN are decoded the same way. 
} 
+0

Pourquoi toutes vos méthodes sont-elles statiques en premier lieu? 'Packet # header' et' Packet # payload' sont tous deux non-statiques, alors pourquoi 'Packet # decode' est-il statique? –

+0

Parce que le décodage renvoie un nouveau paquet lu à partir d'un tampon de données. Je veux dire, il n'y a toujours pas de structure de paquets, c'est comme une méthode d'usine. Il y a aussi une 'méthode' de codage qui convertit l'objet en octets. Celui-ci n'est pas statique. –

+0

Vous pouvez abuser des méthodes des usines statiques. Si 'Command' est abstrait, vous ne pouvez pas utiliser une fabrique statique directement dans la classe pour l'instancier. Vous devrez utiliser une conception similaire à 'Executors', qui expose les usines statiques pour différents sous-types' ExecutorService'. La réponse 'enum' n'est pas vraiment évolutive, surtout si vous avez besoin de centaines de commandes. Une abstraction ('Command') ne devrait pas essayer de fournir sa propre usine, sinon elle sera obligée de connaître ses implémentations concrètes (sous-types), ce qui est une mauvaise conception. –

Répondre

1

Vous pourriez avoir une classe enum qui contient les informations relatives à ce que la commande est et comment le décoder. En utilisant une méthode abstraite au lieu d'un methid statique vous forcer à mettre en œuvre un décodage pour chacun:

public enum CommandType { 
    COMMAND_0(0) { 
     @Override 
     public <T extends Command> T decode(Connection dbConnection, ResponseBuilder.Header header, byte[] data, int offset) throws Exception { 
      // decode Command 0 
      ... 
     } 
    }, 
    COMMAND_1(1) { 
     @Override 
     public <T extends Command> T decode(Connection dbConnection, ResponseBuilder.Header header, byte[] data, int offset) throws Exception { 
      // decode Command 1 
      ... 
     } 
    } 
    ... 
    COMMAND_N(N) { 
     @Override 
     public <T extends Command> T decode(Connection dbConnection, ResponseBuilder.Header header, byte[] data, int offset) throws Exception { 
      // decode Command N 
      ... 
     } 
    } 
    ; 

    private final int commandIdentifier; 

    CommandType(int commandIdentifier) { 
     this.commandIdentifier = commandIdentifier; 
    } 

    public abstract <T extends Command> T decode(Connection dbConnection, ResponseBuilder.Header header, byte[] data, int offset) throws Exception; 


    private static Map<Integer, CommandType> map = new HashMap<Integer, CommandType>(); 
    static { 
     for (CommandType commandType : CommandType.values()) { 
      if (map.get(commandType.commandIdentifier) != null) 
       throw new IllegalStateException("There are several commands with the same identifier"); 
      map.put(commandType.commandIdentifier, commandType); 
     } 
    } 

    public static CommandType fromIdentifier(int commandIdentifier) throws IllegalArgumentException { 
     CommandType commandType = map.get(commandIdentifier); 
     if (commandType == null) 
      throw new IllegalArgumentException("Unkown command identifier"); 
     return commandType; 
    } 

} 

Après cela, vous pouvez simplement utiliser:

Command c = CommandType.fromIdentifier(commandIdentifier).decodedecode(dbConnection, header, data, offset); 

Je pense que ce d'une façon plus élégante de manipulation