Design pattern Command illustré en java 8 et en Javascript & PhantomJS!

COMMAND_designPattern

Rien que le titre est tout un programme!

Ce billet présente des exemples pratiques, en Java8 et en JS, du design pattern (motif de conception) de comportement nommé Command. L'objectif principal de ce design est de découpler le sender (producer) du receiver (consumer). Nous détaillons cela un peu plus loin. A la fin de la première démo, on peut surtout constater que cet objectif est atteint.

Ce billet est composé de deux parties bien distinctes:

  • La première partie illustre la pratique du design pattern à l'aide des lambdas de java 8. & test JUnit,
  • La seconde partie illustre ce même design pattern en Javascript et testé avec PhantomJS

Et oui,  en JS aussi il faut bien concevoir.

Mais avant d'aller plus loin, posons-nous les bonnes questions :

  • C'est quoi le design pattern Command?
  • Dans quels (use case) cas peut-on le rencontrer?

Vous trouvez dans le wiki en anglais une définition assez claire et certaines notes utiles sur l’ambiguïté autour de sa terminologie.

Notez aussi les exemples fournis dans plusieurs langages dont java 8.

Disons tout simplement que ce design permet d'encapsuler une requête (Command) en tant qu'objet qui va jouer le rôle d'un adpater en déléguant le traitement.

On rencontre souvent ce design dans le cas d'une requête (Event, Action ou Command) à traiter par le receiver (handlers).

Comme par exemple dans les applications ayant des interfaces graphiques riches composées de barres (ou toolbar) menus et menus contextuels,...

Un élément central dans ce design est l'objet Command qui sert à encapsuler et à regrouper les invocations.

Et pour réaliser le découplage entre le sender (invoker) et le receiver, l'objet Command assure donc aussi le rôle d'un adapter (un autre design).

Passons à la pratique pour illustrer tout ça...

PREMIÈRE PARTIE. Démo  ( java 8 )

USE CASE:

Le client adresse une requête pour archiver des documents. Une fois que le service chargé de l'envoi aura effectué son travail, le service traitera la demande d'archive. Dans cette démo les interfaces/objets en jeu sont :

1- Une interface fonctionnelle (functional interface) CmdArchiv ayant une seule méthode abstraite execute(). On donnera au moins une implémentation concrète.

2- Un objet Sender (Invoker) permettant l'envoi d'une demande d'archive via CmdArchiv.

3- Un objet Receiver pour justement  exécuter le traitement d'archivage.

4- Le client (ici test JUnit) qui initie la demande.

Il est important de garder à l'esprit que les objets  Sender (Invoker) et Receiver ne doivent avoir aucune relation.

Le Receiver n'a nul besoin de connaitre le contexte du Sender! Voici les détails en commençant par le Receiver:

Interface Receiver (mono-interface)

public interface IReceiver {
   boolean traiterArchive();
}

A noter que son implémentation sera donnée par une lambda.

Interface fonctionnelle CmdArchiv

@FunctionalInterface
public interface CmdArchiv {     
    boolean execute(); 
}

Et voici une première implémentation:

import com.netapsys.xxxx.IReceiver;

public class CmdArchivImpl1 implements CmdArchiv{
    private IReceiver receiver;

    public CmdArchivImpl1(IReceiver rec){
	this.receiver = rec;
    }
    @Override
    public boolean execute() {
	return receiver.traiterArchive();
    }
    //getters setters omis
}

Il est important de souligner que la méthode execute ne fait que déléguer l'exécution du traitement d'archivage au Receiver via la méthode traiterArchive.

Sender (mono-interface)

import com.netapsys.xxxxxxx.CmdArchiv;
public interface ISender {
    boolean send(CmdArchiv cmd);
}

On peut déjà à ce stade relever que les objets Sender et Receiver n'ont aucune relation.

Enfin, voici le client JUnit:

import org.junit.*;
import com.netapsys.xxxx.*;
public class ClientTest {
    @Test
    public void testCmdPattern() {
	//define receiver by lambda
	IReceiver receiver= ()-> {
	    System.out.println("go receiver!");
	    return true; 
	};	
	//define concrete command 
	final CmdArchiv cmdArchivV1=()->receiver.<code>traiterArchive

Le code est suffisamment commenté pour ne pas rajouter des explications.

L'assertion du test montre que le Sender retourne true via sa méthode send (qui, ici, appelle execute() de CmdArchiv).

Pour conclure, en définissant correctement les responsabilités, à l'aide du design Command nous avons pu découpler les objets Sender et Receiver.

C'est à dire découpler la phase de préparation du contexte (paramètres) de l'action, de l'action à exécuter (éventuellement plus tard).

L'avantage est que tout cela facilite grandement les évolutions métier et la maintenance applicative.

Gardons toujours à l'esprit le principe  SRP: Single Responsability Principle!

PS. Vous pouvez trouvez ici ou plus d'infos sur ce design pattern.

Vous retrouvez l'exemple Javascript dans un prochain billet, très prochainement 😉

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.