Pour illustrer le problème, disons que l'interface de la classe distante s'appelle « Toto », avec les méthodes « doSomething(int, int) » et « doSomethingElse(String) ».
On faisait jusqu'ici un appel réseau pour chaque invocation :
Avec le code Java ci-dessous :
final Toto toto = <proxy>; // proxy pour appel distantAprès s'être aperçu qu'on pouvait gagner en performances, nous décidons que du point de vue du réseau il n'y aura qu'un appel, qui agrègera les invocations :
toto.doSomething(2, 5); // 1er appel
toto.doSomething(14, 8); // 2e appel
toto.doSomethingElse("Fantastic!"); // 3e appel
toto.doSomething(0, 1); // 4e appel
Eh bien je ne sais pas pourquoi, mais pour faire ça certains pensent à écrire la chose suivante :
final Commands commands = new Commands(Toto.class);C'est tout à fait dommage, car on perd l'avantage de la vérification à la compilation : si on se trompe dans une chaîne de caractères qui contient un nom de méthode, ou dans le type des paramètres d'une méthode, on ne s'en apercevra qu'à l'exécution.
commands.storeCommand("doSomething", 2, 5); // 1er
commands.storeCommand("doSomething", 14, 8); // 2e
commands.storeCommand("doSomethingElse", "Fantastic!"); // 3e
commands.storeCommand("doSomething", 0, 1); // 4e
CommandUtils.executeCommands(<proxy>, commands);
En fait il faut s'arranger pour pouvoir coder ainsi :
final Toto toto = FutureUtils.newFuture(Toto.class);C'est-à-dire qu'il faut s'appuyer le plus possible sur ce qui compile... déjà.
toto.doSomething(2, 5); // 1er appel
toto.doSomething(14, 8); // 2e appel
toto.doSomethingElse("Fantastic!"); // 3e appel
toto.doSomething(0, 1); // 4e appel
FutureUtils.execute(<proxy>);
Un très bon modèle pour penser de cette façon, est EasyMock : ce framework d'aide aux tests permet d'enregistrer les appels à contrôler par du code d'appel ! On a donc la vérification de la syntaxe à la compilation, puis évidemment la vérification de la séquence à l'exécution.
2 commentaires:
Tout à fait d'accord.
En particulier on retrouve souvent ce genre de problématique dans des applications client lourd avec le controller du framework MVC implémenter sur le serveur.
Dans ce cas le modèle est souvent sérializer sous forme de clef/valeur.
Il me semble qu'une approche par interface reste la meilleur des solutions.
Il faut alors mettre en place des systèmes de génération de code et de sérialisation générique entre le client et le serveur.
Bon je sais que c'est pas clair, mais en gros ça veut dire que je suis d'accord et qu'il faut dès le départ avoir une approche d'un petit socle de communication prenant en charge la problématique de vérification à la compilation ...
Qui suis je ... y a un petit indice dans le commentaire...
Super blog david, continues comme ça.
Bonjour Oliv qui fais du Subversion et joues au poker,
oui c'est du délire les mecs qui stockent tout dans des Map<String, Object>... Sans parler de ceux qui n'utilisent même pas Java 5, bien sûr.
Pour la génération de code, je me suis fait un bête plugin Maven qui me crache des JavaBeans et qui commence à être bien rodé, mais Google a cassé le marché cette semaine avec ses Protocol Buffers.
Parce qu'écrire soi-même, ne seraient-ce que des interfaces, avec tous les getXxx() et setXxx(), c'est quand même assez pénible. Sans compter qu'à chaque fois on se dit que « ça serait plus rapide en Ruby ». Donc code generation rulez.
Du coup l'approche par interfaces n'a pas trop d'intérêt : on peut tout à fait produire des classes par génération. La manipulation de bytecode permet de rajouter au runtime tout ce qui manque, éventuellement.
Pour la sérialisation, je suis assez partisan de l'approche des Protocol Buffers, c'est-à-dire un format propriétaire (moi je fais du YAML), transverses aux technologies (Python, C++, Java, PHP...) et déclinable en tout ce qu'on veut de standard (SOAP...).
Et ça va, sinon ?
Enregistrer un commentaire