Thématiques principales

dimanche 5 novembre 2017

Design pattern : Strategie

J'avais déjà écris un article sur la conception logicielle dans lequel j'avais évoqué l'utilisation de design pattern pour la réalisation des architectures logicielles. Je ne vais pas revenir sur l’intérêt général de leur utilisation que j'ai déjà développer dans l'article précédent. Mais dans l'idée de fournir des explications sur leur utilisation je vous propose de nous pencher sur l'un d'eux : le pattern Stratégie.

Il s'agit d'un pattern comportemental, c'est a dire qu'il facilite la modélisation des comportements de notre logiciel.

Le pattern strategie est un pattern simple que l'on applique naturellement si l'on connait bien le concept objet. En effet, en programmation orienté objet, on préconise fortement la réification sous la forme d'objet les structures, les relations mais aussi les comportements. Et c'est cela que fait essentiellement le pattern stratégie : il permet le découplage du comportement des objets qui naturellement les porteraient en les réifiant.

Ainsi, le pattern stratégie nous permet non seulement de maintenir les codes comportementaux séparément de la structure logiciel mais du coup il en facilite le partage et la permutation.





Une classe cliente d'un ensemble de stratégies va alors appeler chacune d'elle en fonction des services qu'elle aura a fournir sur soit les éléments externes au client de façon a traiter spécifiquement des données ou sur elle même afin de limiter les dépendances le client servant alors de pont (mais nous reviendrons sur ce pattern également)

Partons du cas suivant:

public class Client {
public Client()
{
}
public void service1()
{
System.out.println("service1");
}
public void service2()
{
System.out.println("service2");
}
public void serviceN()
{
System.out.println("serviceN");
}
}

Classiquement on va réifier les fonctions services avec le pattern stratégie comme suit. A noter que  l'emploi d'un classe abstraite ou d'une interface tiens ici a la façon de modéliser le DSL, si les stratégies sont d'ordre technique on tendra vers une interface, si on tend vers du fonctionnel on utilisera plutôt vers une classe abstraite. (Nous traiterons les différences entre fonctionnel et technique dans un article dédié):

public abstract class AbstractStrategie {
abstract void apply();
}

Avec les stratégies suivantes:

public class Strategie1 extends AbstractStrategie{
@Override
void apply() {
System.out.println("service1");
}
}
public class Strategie2 extends AbstractStrategie{
@Override
void apply() {
System.out.println("service2");
}
}
public class StrategieN extends AbstractStrategie{
@Override
void apply() {
System.out.println("serviceN");
}
}
Et du coup le client utilisera les stratégies associées soit en les préparant soit en creeant le comportement a chaud en approche Lazy:

public void service1()
{ new Strategie1().apply();
}
public void service2()
{
new Strategie2().apply();
}
public void serviceN()
{
new StrategieN().apply();
}

Bon il faut avouer qu'ainsi, on manque un peu de souplesse, mais ce n'est pas le propos au final, apres on est libre de créer au sein du client ou d'un registre dédié la responsabilité de créer et fournir dynamiquement la bonne stratégie mais ce choix ne relève plus du pattern stratégie et dépend de la façon dont les service du client seront exposés.

De la  façon, une stratégie peut être plus ou moins complexe et la fonction apply doit avidement porter plus de sens, en portant plus de paramétrés, ou des données de retour.

Cela dit, cette manière d’implémenter le pattern stratégie est une façon classique en version java mais avec les lambda expression, nous avons maintenant la possibilité de le simplifier (attention ce qui va suivre est une variante d’implémentation)

On définit une interface fonctionnelle:

@FunctionalInterface
public interface Strategy {
void apply();
}

Et on créer les lambda expression que le client pourra choisir:

public class Client {
private Strategy[] strategies= {
()-> System.out.println("service1"),
()-> System.out.println("service2"),
()-> System.out.println("serviceN")
};
public Client() {
}
public void service1() {
strategies[0].apply();
}
public void service2() {
strategies[1].apply();
}
public void serviceN() {
strategies[2].apply();
}
}

Voila, ce petit article avait pour but d'introduire le pattern de conception que tout le monde utilise sans vraiment le savoir avec une petite variante avec des lambda expression. Si vous souhaitez avoit d'autre exemple de ce genre je vous invite a lire cet article.

Aucun commentaire:

Enregistrer un commentaire