2012-10-28 1 views
1

Je travaille sur un cadre dans lequel nous avons le concept d'une tâche. L'espoir est que beaucoup de gens vont écrire des tâches, et pour ce faire, ils auront simplement besoin de mettre en œuvre l'interface de tâche. Les tâches doivent être déclenchées via leur nom, ce qui signifie qu'il doit y avoir un registre central de task-name-> task-class quelque part. Actuellement j'utilise un Enum pour le faire, mais cela nécessite que les implémenteurs de tâches ajoutent du code dans l'Enum pour chaque tâche qu'ils implémentent, ce qui est grossier.Expédition par chaîne sans réflexion ou code à plusieurs endroits

Une autre option que j'ai trouvée est de rechercher par réflexion pour toutes les classes qui implémentent l'interface Tâche et les enregistrer à partir de là. Cela me semble aussi dégoûtant et je pense qu'il doit y avoir un meilleur moyen.

Un initialiseur statique serait parfait s'il ne nécessitait pas que la classe soit référencée au moins une fois avant de s'exécuter.

Les annotations sont une autre option, mais elles nécessitent plus de travail de réflexion, ou la modification du processus de construction si nous devions compiler time code-gen pour faire le répartiteur.

Des idées plus simples?


Une version simple de ce que je fais est maintenant ci-dessous, pour donner une idée du flux de contrôle que je veux (a percuté une seule classe pour simplifier):

class Disp { 
    interface Task { 
     public String doTask(); 
    } 

    static class HelloTask implements Task { 
     public String doTask() { 
      return "Hello"; 
     } 
    } 

    static class ByeTask implements Task { 
     public String doTask() { 
      return "Bye"; 
     } 
    } 

    static class TaskDispatcher { 
     static enum Tasks { 
      Hello { 
       Task getTask() { 
        return new HelloTask(); 
       } 
      }, 
      Bye { 
       Task getTask() { 
        return new ByeTask(); 
       } 
      }; 
      abstract Task getTask(); 
     }; 

     public static Task getTask(String taskName) { 
      return Tasks.valueOf(taskName).getTask(); 
     } 
    } 

    public static void main(String[] args) { 
     System.out.println("Hello task says: "+TaskDispatcher.getTask("Hello").doTask()); 
     System.out.println("Bye task says: "+TaskDispatcher.getTask("Bye").doTask()); 
    } 

} 

Quel serait beaucoup plus agréable est si le code pour enregistrer (disons) HelloTask pourrait vivre seulement dans HelloTask et ne pas avoir à couler dans TaskDispatcher (Comme dans le vrai projet ce sont plusieurs fichiers différents)

Répondre

0

J'ai implémenté ce genre de chose dans le passé en ayant simplement un fichier de configuration qui contient le les correspondances de noms à classnames:

Task1=com.acme.tasks.MyTask1 
Task2=com.acme.tasks.more.MyTask2 

Il est utilisé pour alimenter le Registre, vous pouvez le faire class.forName() sur le nom de classe, et de créer une nouvelle instance de la classe de tâche spécifique.

L'ajout d'une nouvelle tâche signifie simplement l'ajout de la nouvelle classe de tâches au chemin de classe et l'ajout d'une ligne au fichier de configuration - aucune recompilation du code existant n'est nécessaire.

+0

C'est un peu plus propre que d'ajouter à l'Enum, mais encore dommage d'avoir à ajouter des choses à plusieurs endroits pour implémenter une nouvelle tâche. – nick

0

N'ayez pas peur d'utiliser des cadres! Avec Spring, il ne s'agit que de quelques lignes de code. Chaque Task doit être annoté (il ne doit pas être @Service du printemps) et tout ce que vous avez à faire dans votre répartiteur (après bootstrapping le contexte et permettant la numérisation des composants) est la suivante:

class TaskDispatcher { 
    @Autowired 
    List<Task> tasks; 

    //... 
} 

Spring trouvera automatiquement tous Task s sur le CLASSPATH et injectez-les pour vous. Vous pouvez utiliser le nom de bean/class comme identifiant ou tout ce que vous voulez.

+0

Je suppose que le printemps va le faire par la réflexion droite? Je peux facilement l'appliquer par moi-même, et je pourrais suivre cette voie, mais je pensais qu'il devrait y avoir une façon plus propre. – nick

+0

Donc, j'ai eu un bash à l'aide de cette méthode, et je suis bloqué parce qu'il semble impossible de ne pas avoir à instancier une instance de chaque tâche pour remplir la liste. Ce que je veux vraiment, c'est une liste >, donc je peux configurer une configuration que la tâche veut dans son constructeur, puis appeler newInstance sur l'un des éléments de la liste. Cela ne semble pas possible au printemps cependant. – nick

0

Vous pouvez consulter le mécanisme du fournisseur de services, par exemple dans this SO post. En utilisant ceci, vous pouvez créer une interface à travers laquelle les implémenteurs de Task notifient votre code central sur toutes les tâches qu'ils implémentent.

Je suppose que chaque implémenteur contribuera son propre JAR, cette technique suppose un tel scénario.

+0

Le tutoriel lié là ne semble plus exister, et je ne comprends pas ce qu'ils essaient de faire à partir du contexte de la question. Bien que certaines tâches puissent être dans leur propre fichier jar, elles ne doivent pas l'être, cela ne semble donc pas être la meilleure option. Merci quand même! – nick

Questions connexes