Thématiques principales

Affichage des articles dont le libellé est strategie. Afficher tous les articles
Affichage des articles dont le libellé est strategie. Afficher tous les articles

samedi 5 mai 2018

Ioc, Inversion de controle

Dans les articles précédents, nous avons à traiter des sujets se rapportant à l'écosystème Java et entre autre a JEE avec JMS. Bien sûr nous ne nous arrêterons pas là et nous nous intéresserons à JPA, le JSP, les Servlet et les EJB. Cependant si ces concepts sont le cœur de JEE, il serait dommage de ne pas traiter également des alternatives possibles à JEE, comme par exemple Spring.

Dans cet article, nous n’allons pas traiter directement Spring mais introduire quelques concepts clefs afin d'appréhender au mieux les mécanismes qui en sont les fondamentaux : l’inversion de contrôle et l’injection de dépendance.

Commençons par l’inversion de contrôle. Nous l’avions évoqué dans un article précédent traitant des principes SOLID [1], nous avions traité de la problématiques de l’inversion de dépendances (principe D) qui par la construction d’entité abstraite, nous permet de découpler le code afin qu’il respecte au mieux le principe de responsabilité unique (principe S). Nous avions évoqué justement qu’en poussant le raisonnement plus loin, ce principe nous permet d’aller jusqu'à l’inversion de contrôle.

Bien sur il existe des patterns très bien fait qui nous permettent facilement de faire de l’inversion de contrôle comme par exemple le pattern Visiteur [2] ou le pattern Stratégie [3] couplé à un pattern factory [4]. C’est par cette deuxième approche que nous allons présenter l’inversion de contrôle (et donc implicitement de dépendances)

Posons le problème. Imaginons une voiture capable de rouler et pour cela, bien sur la voiture va employer un moteur. La première approche qui aura le mérite de marcher sera probablement d'écrire un code proche de celui ci (en groovy):


class Voiture{

   Moteur moteur=new Moteur()

   def drive() {
       this.moteur.active()
       println("la voiture avance")
   }
}

class Moteur
{
   def active() {
       println("le moteur tourne");
   }
}

def voiture=new Voiture()
voiture.drive()

Même s’il respecte une certaine décomposition, ce code souffre d’un problème de couplage fort entre les classes Voiture et Moteur. En effet, dans ce cas, il est impossible de prévoir une voiture avec un moteur qui serait différent en sortie d’usine! Il faudrait ajouter un moyen permettant de changer ce moteur après la construction comme une méthode supplémentaire du type changeMoteur. Ce n’est pas acceptable.

Ainsi pour répondre à cela, on va suivre le principe I de SOLID en rendant abstrait le moteur dans la voiture modélisant ainsi qu’il sera possible d’utiliser des moteurs différents.

Cela donne donc ceci:


class Voiture{

   Moteur moteur;

   def Voiture(Moteur m)
   {
       this.moteur=m;
   }

   def drive() {
       this.moteur.active()
       println("la voiture avance")
   }
}

abstract class Moteur{
   def abstract active()
}

class MoteurX extends Moteur{
   def active() {
       println("le moteur X tourne");
   }
}
class MoteurY extends Moteur{
   def active() {
       println("le moteur Y tourne");
   }
}

new Voiture(new MoteurX()).drive()
new Voiture(new MoteurY()).drive()

On a réussi à découpler le moteur de la voiture lors de sa création mais on a pas vraiment rationalisé la construction des moteurs pour les voitures. C’est donc la que va intervenir le pattern Factory en fournissant une entité unique permettant la création des moteurs (et cela nous permet de respecter le principe S). Voici donc comment évolue notre code, suite a l’ajout d’une factory:

class MoteurFactory
{
   def buildMoteurX()
   {
       return new MoteurX()
   }

   def buildMoteurY()
   {
       return new MoteurY()
   }
}

def mFactory=new MoteurFactory()

new Voiture(mFactory.buildMoteurX()).drive()
new Voiture(mFactory.buildMoteurX()).drive()

Voilà, les voitures n’ont plus la responsabilité de construire leur moteur, c’est la factory qui s’en chargera nous permettant de choisir le moteur le plus adapté. Du point de vue de la voiture, pour elle, cela reste transparent, puisque tous les moteurs que l’on peut lui donner respecte la charte d’utilisation (on respecte alors le principe I).

L’inversion de contrôle et l’inversion de dépendances ne sont pas des principes très complexe à appréhender ni à mettre en oeuvre pour le peu qu’on les démystifient . Il s’agit surtout d’un principe de décomposition [5], dont le but est le respect des principes SOLID.

Pour l’injection de dépendances, je vous invite au prochain rendez vous, dans un article suivant.

Références

[1] http://un-est-tout-ethttps://blog.imirhil.fr/2013/05/19/inversion-de-controle-cest-bon-mangez-en.html-tout-est-un.blogspot.fr/2018/04/solid.html
[2] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/12/design-pattern-visiteur.html
[3] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/11/design-pattern-strategie.html
[4] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/11/design-pattern-factory.html
[5] https://blog.imirhil.fr/2013/05/19/inversion-de-controle-cest-bon-mangez-en.html

lundi 20 novembre 2017

Design pattern : Etat

Le pattern Etat ou pattern state est un pattern comportemental. Il permet dynamiquement de changer le comportement du système logiciel qui l'utilise au delà du pattern stratégie qui n'est qu'une délégation du comportement. Le pattern état va bien plus loin car l’état est gérer par le client lui-même et les changements de mode de fonctionnement (donc d’état) sont également a la responsabilité du client. Le pattern stratégie ne permet pas ce niveau d'adaptation.



A noter que si état et stratégie sont bien des patterns différents, on peut facilement imaginer une utilisation du pattern stratégie pour répondre au besoin comportemental de chacun des états si des contexte d'utilisation sont différents eux aussi.

Par exemple, si l'on modélise le comportement d'un robot au travers d'un pattern état qui représenteront des modes de marche, on emploiera alors le pattern stratégie pour se placer dans un contexte réel et un contexte simulé. La différence entre les deux patterns se situe la.

A noter que personnellement je trouve ce modèle du pattern état très simple et j'aurai plutôt une approche IDM (Ingénierie Dirigée par les Modèles) pour la modélisation d'un système a Etat complet exploitant toute la sémantique des FSM (Finite State Machine). Je traiterais de ces sujets dans des articles dédiés.

dimanche 5 novembre 2017

Design pattern : Le Composite

Ce pattern de concept est probablement comme le stratégie, l'un des plus courant et celui que l'on identifie le plus vite.

Contrairement au pattern stratégie qui était de type comportemental, il s'agit ici d'un pattern structurel permettant d'agencer une structure objet de façon logique, dans le principe de l'objet dans l'objet.

Ainsi on trouvera souvent ce pattern des qu'il s'agit de donner des propriétés générales a des objets contenus dans des objets portant les même propriétés. L'exemple classique sont les IHM structurées souvent de la sorte de façon a faciliter l'agencement des éléments graphiques.

On retrouvera ce pattern également lorsqu'il faudra organiser les objets de façon arborescente afin de hiérarchiser les éléments.



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.