0% ont trouvé ce document utile (0 vote)
184 vues61 pages

Design Principles

Ce document traite des principes de conception logicielle SOLID et des principes de couplage et de cohésion. Il présente les concepts de couplage faible et fort ainsi que de cohésion forte. Le document décrit également les cinq principes SOLID, notamment le principe de responsabilité unique et le principe ouvert/fermé.

Transféré par

salmen bani
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
184 vues61 pages

Design Principles

Ce document traite des principes de conception logicielle SOLID et des principes de couplage et de cohésion. Il présente les concepts de couplage faible et fort ainsi que de cohésion forte. Le document décrit également les cinq principes SOLID, notamment le principe de responsabilité unique et le principe ouvert/fermé.

Transféré par

salmen bani
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
Vous êtes sur la page 1/ 61

Architecture Logicielle et

Design Patterns
Design Principles

I.mouakher
Introduction
 A good software design organizes the code in a way that it is "easy to
understand, change, maintain and reuse.“
 Values of a Good Design - Communication, Simplicity, Flexibility
 Design principles are (often opinionated) guidelines derived from experience
of programmers about software design that usually take the form of do's and
don'ts.
 The Design principles are the Commandments of OO Programming.

2
COUPLAGE & COHÉSION

3
Couplage & Cohésion
 Nous allons explorer deux principes fondamentaux
sur la cohésion et le couplage. Le chemin vers un logiciel orienté objet
modifiable et maintenable commence par des classes hautement cohérentes
et faiblement couplées.
 Deux critères absolus :
 1) Cohésion
 2) Couplage

 ils portent sur la notion de module :


 Fonction, Classe, Paquet, Composant ou Noeud.
 Ces deux critères sont difficiles à mesurer automatiquement.

4
Couplage
 Mesure la force d'interaction entre les modules.
 Le degré de dépendance de chaque module aux autres modules.
 Il faut minimiser le couplage dans une conception.
 Questions associées :
 Comment les modules collaborent ensemble ?
 Qu'ont-ils besoin de connaître les uns des autres?
 Indice
 La liste des importations est un bon indicateur de la force de couplage: e.g, nombre
d'inclusions (#include en C++, C#), import (Java, Python).

5
Couplage
Couplage fort
 Quand une classe A est lié à une classe B, on dit que la classe A est fortement couplée à la classe B.
La classe A ne peut fonctionner qu’en présence de la classe B. Si une nouvelle version de la classe B
(soit B2), est crée, on est obligé de modifier dans la classe A
 Modifier une classe implique:
 Il faut disposer du code source
 Il faut recompiler, déployer et distribuer la nouvelle application aux clients
 Ce qui engendre un cauchemar au niveau de la maintenance de l’application
Couplage faible
 Considérons une classe A qui implémente une interface IA, et une classe B qui implémente une
interface IB. Si la classe A est liée à l’interface IB par une association, on di que la classe et la classe B
sont liées par un couplage faible. La classe B peut fonctionner avec n’importe quelle classe qui
implémente l’interface IA.
 Avec le couplage faible, on peut créer des applications fermée à la modification et ouvertes à
l’extension
Faible couplage
 Le faible couplage favorise :
 la faible dépendance entre les classes,
 la réduction de l'impact des changements dans une classe,
 la réutilisation des classes ou modules.
 Pour affaiblir le couplage, il faut :
 diminuer la quantité de paramètres passés entre les modules,
 éviter d'utiliser des variables globales (par exemple, si une mauvaise valeur est
assignée, détecter la fonction/classe incorrecte est plus difficile), il vaut mieux
passer les valeurs en paramètres.
Forte cohésion
 La cohésion mesure la compréhensibilité des classes. Une classe doit avoir des
responsabilités cohérentes, et ne doit pas avoir des responsabilités trop variées. Une classe
ayant des responsabilités non cohérentes est difficile à comprendre et à maintenir.
 La forte cohésion favorise :
 la compréhension de la classe,
 la maintenance de la classe,
 la réutilisation des classes ou modules.

 Sinon, la faible cohésion est une situation dans laquelle un élément donné a
trop d'activités sans rapport avec ses responsabilités. Les éléments avec une
faible cohésion souffrent souvent d'être difficile à comprendre, difficile à
réutiliser, difficile à maintenir et réfractaire au changement
SOLID PRINCIPES

9
SOLID
 Bien qu'ils s'appliquent à toute conception orientée objet, les principes SOLID peuvent
également former une philosophie de base pour des méthodologies telles que le
développement agile ou le développement de logiciels adaptatifs.
 La théorie des principes SOLID a été introduite par Martin dans son article Design Principles
and Design Patterns de 2000.

10
SOLID
 Introduit par Robert C. Martin.
 En programmation orientée objet, SOLID est un acronyme mnémonique qui regroupe cinq
principes de conception
 Single responsibility principle (Responsabilité unique)
 une classe, une fonction ou une méthode doit avoir une et une seule responsabilité
 Open/closed principle (Ouvert/fermé )
 une entité applicative (classe, fonction, module ...) doit être fermée à la modification directe mais ouverte à
l'extension
 Liskov substitution principle (Substitution de Liskov)
 une instance de type T doit pouvoir être remplacée par une instance de type G, tel que G sous-type de T, sans
que cela ne modifie la cohérence du programme
 Interface segregation principle (Ségrégation des interfaces)
 préférer plusieurs interfaces spécifiques pour chaque client plutôt qu'une seule interface générale
 Dependency inversion principle (Inversion des dépendances)
 il faut dépendre des abstractions, pas des implémentations
12
Responsabilité unique (SRP: Single Responsibility
Principle)
 Le principe de responsabilité unique, réduit à sa plus simple
expression, est qu'une classe donnée ne doit avoir qu'une seule
responsabilité, et, par conséquent, qu'elle ne doit avoir qu'une
seule raison de changer.
14
Exemple
public class Customer
{ public int Id;
public string Name;
public bool Active;
public void ActivateCustomer() { Active = true; }

public void InactivateCustomer() {Active = false; }  La classe Customer a deux responsabilités,


public void AddCustomer()
{// Some implementation here (database) ... }
les règles de gestion et la persistance de la
public void DeleteCustomer() base de données.=> SRP violé
{ // Some implementation here (database) ... }
}

15
Exemple
public class Customer
{ public int Id;
public string Name;
public bool Active;
public void ActivateCustomer() { Active = true; }

public void InactivateCustomer() {Active = false; }


 SRP garanti car les responsabilités ont été
}
réparties et chaque classe n’a qu’une seule
public class CustomerRepository
{ raison de changer.
public void AddCustomer(Customer customer)
{ // Some implementation here (database) ... }

public void DeleteCustomer(Customer customer)


{ // Some implementation here (database) ... }
}

16
How does this principle help us to build better
software?
 Let's see a few of its benefits:
 Testing – A class with one responsibility will have far fewer test
cases.
 Lower coupling – Less functionality in a single class will have
fewer dependencies.
 Organization – Smaller, well-organized classes are easier to
search than monolithic ones.
Ouvert/fermé (OCP: Open/closed Principle)
 Définition: Les modules qui se conforment au principe
ouvert/ferme ont deux attributs principaux.
 Ils sont "ouverts pour l'extension". Cela signifie que le comportement du
module peut être étendu, que l'on peut faire se comporter ce module de
façons nouvelles et différentes si les exigences de l'application sont
modifiées, ou pour remplir les besoins d'une autre application.
 Ils sont "Fermés à la modification". Le code source d'un tel module ne
peut pas être modifié. Personne n'est autorisé à y apporter des
modifications. Bref, il ne faut pas briser la logique de l’héritage.
Ouvert/fermé (OCP: Open/closed Principle)
 Ouvert à une extension - vous devez concevoir vos classes de manière à
ce que de nouvelles fonctionnalités puissent être ajoutées à mesure que
de nouvelles exigences sont générées.
 Fermé pour modification - Une fois que vous avez développé une classe,
vous ne devez jamais la modifier, sauf pour corriger des bogues.
 La conception et le code doivent être faits de manière à ce que de
nouvelles fonctionnalités soient ajoutées avec un minimum ou aucun
changement dans le code existant
 Lorsqu'il est nécessaire d'étendre les fonctionnalités - évitez les
couplages étroits, n'utilisez pas la logique if-else/switch-case, effectuez la
refactorisation du code si nécessaire.
Exemple 1
public void ProcessPayment()
{ if (PaymentMethod == PaymentMethod.VisaCard)
{ // Some implementation heren}
else if (PaymentMethod == PaymentMethod.MasterCard)
{// Some implementation heren}
else if (PaymentMethod == PaymentMethod.Cash)
{ // Some implementation here }
// Some implementation here
}
 => OCP violé : le code est ouvert à la modification, si un nouveau mode de paiement est ajouté, la
classe doit être modifiée.

21
Exemple 1
public void ProcessPayment()
{ // Some implementation here
PaymentMethod.ProcessPayment();
// Some implementation here
}

=> OCP garanti, car si un nouveau mode de


paiement est ajouté, la classe n'est pas modifiée.

22
Exemple 1

 Que va-t-il se passer si on a


besoin d’un nouveau
évènement?
 Que va-t-il se passer si on a
trop d’évènements à gérer?
 Le code semble
étrangement répétitif, n'est-
ce pas ?
Exemple 1 - OCP violé
Exemple 1 - OCP garanti!

 Polymorphisme est votre ami!


 Contrôle des types (« Type Checking ») est toujours une indication pour une hiérarchie.
Ouvert/fermé (OCP: Open/closed Principle)
 Les entités logicielles doivent être ouvertes aux extensions, mais
fermées aux modifications.
 Pensez toujours :
« qu’est-ce qui va se passer si l’entité change? »
 L’héritage et le polymorphisme favorisent l’extension.
 Pattern à appliquer – modèle de stratégie, méthode de modèle
La substitution de Liskov (LSP : Liskov Substitution
Principle)
 « Si cela ressemble à un canard, sonne comme un canard, mais a besoin de
piles, vous avez probablement la mauvaise abstraction. »
 Les fonctions qui utilisent des références à des classes de base doivent
pouvoir utiliser des objets de classes dérivées sans le savoir.
 Les préconditions ne peuvent pas être renforcées dans le type dérivé.
 Les méthodes dérivées ne peuvent pas attendre plus que les méthodes de base.
 Les postconditions ne peuvent pas être plus faibles dans le type dérivé.
 Les méthodes dérivées ne peuvent pas fournir moins que les méthodes de base.
La substitution de Liskov
 Définition: Les sous-types doivent être remplaçables par
leur type de base.
 Là, je vais en voir un ou deux (ou plus) dire: « Oui, mais à
partir du moment où ma classe S hérite de ma classe T »,
je dois pouvoir caster S en T et là ça va marcher...
Exemple
 The BankingAppWithdrawalService serves the withdrawal functionality to its users:

 Unfortunately, there is a problem with extending this


design. The BankingAppWithdrawalService is aware of the
two concrete implementations of account. Therefore,
the BankingAppWithdrawalService would need to be
changed every time a new account type is introduced.

 Using the Open/Closed Principle to Make the Code


Extensible
 Consequently, the BankingAppWithdrawalService is open
for the extension with new account types, but closed for
modification, in that the new types don't require it to
change in order to integrate.
Exemple
 The bank now wants to offer a high interest-earning fixed-term deposit account to its
customers.
 To support this, let's introduce a new FixedTermDepositAccount class. A fixed-term deposit
account in the real world “is a” type of account. This implies inheritance in our object-oriented
design.
 So, let's make FixedTermDepositAccount a subclass of Account:
Exemple

 LSP violé
Exemple : LSP garanti
 Refactored BankingAppWithdrawalService
Séparation des Interfaces (ISP: Interface
Segregation Principle)
 « Tu veux que je mette ça où? »
 De nombreuses interfaces spécifiques aux clients valent mieux
qu’une seule interface.
Séparation des Interfaces (ISP: Interface
Segregation Principle)
 Définition: Les clients d'une entité logicielle ne doivent
pas avoir à dépendre d'une interface qu'ils n'utilisent pas.
 Ce principe apporte principalement une diminution du
couplage entre les classes (les classes ne dépendant
plus les unes des autres).
 On peut comprendre que l’ISP est lié avec le SRP par le
fait que l’interface peut définir plusieurs concepts qui ne
sont pas nécessairement liés.
Exemple
 Dans l’exemple ci-dessous, la classe
Vehicle a deux comportements. Nous
pouvons démarrer la voiture ou allumer
les lumières.
 En supposant qu’une deuxième classe
aurait la responsabilité d’allumer les
lumières comme suit:
 Afin de respecter au maximum l’ISP, il
faut se demande pourquoi une classe
responsable d’allumer les lumières
devrait savoir qu’un véhicule peut être
démarrer. 37
Exemple
 On peut alors extraire l’interface afin que notre classe ne
connaisse que ce dont elle a besoin:

38
Inversion des dépendances (DIP:
Dependency Inversion Principle)
 « Souderiez-vous une lampe directement sur le câblage électrique dans le
mur? »
 Définition: Les modules de haut niveau ne doivent pas dépendre des
modules de bas niveau. Les deux doivent dépendre d'abstractions. Les
abstractions ne doivent pas dépendre des détails. Les détails doivent
dépendre des abstractions.
 DIP says that modules should depend upon interfaces or abstract classes,
not concrete classes. It's an inversion because implementations depend
upon abstractions and not the other way round. Instead of high-level modules
depending on low-level modules, let's decouple them and make use of
abstractions.
DIP

 Une classe doit dépendre de son abstraction, pas de


son implémentation.
 Autrement dit, on évite de passer des objets en
paramètre lorsqu’une interface est disponible. Passer
en paramètre une interface permet d’être certain que
l’objet que tu manipules, peu importe son type, aura
les bonnes méthodes associées.

41
DIP
 Le DIP ou principe d’inversion de dépendance nous dit
que les dépendances d’une classe ne devraient jamais
être concrètes. Puisqu’elle ne doit pas connaître
l’implémentation de ses dépendances, nous pouvons
nous assurer du respect de ce principe en implémentant
le mécanisme d’injection de dépendances

42
Exemple
 Supposons que vous avez une classe responsable de la
journalisation. Cette classe est utilisée dans un service
afin de journaliser les entrées et sorties:
 Ici, Logger et SomeService sont
fortement couplées. Le problème
surgira lorsqu’il faudra journaliser
dans un fichier au lieu de la console,
surtout si Logger est utilisée comme
telle partout dans l’application. 43
Exemple
 Par contre, si nous dépendons d’une interface:

 Malheureusement, nos classes


restent encore couplées. Pour sortir
l’implémentation complètement, on
doit inverser les dépendances en
implémentant l’injection:

44
Exemple
 Et voilà! SomeService n’a plus connaissance de la
technologie utilisée pour la journalisation.

45
Autres Principes
 KISS ( keep it simple, stupid)
 DRY (don’t repeat yourself)
 YAGNI (you ain't gonna need it)
 ….

46
QCM

47
Question 1
 Un nouveau type d'indemnités de congé doit être ajouté dans
un système logiciel pour les ressources humaines. Le code
d'origine doit être considérablement modifié pour supporter
la fonctionnalité. Quel est le principe de conception SOLID
enfreint dans cette situation ?
 Ouvert/fermé
 Inversion des dépendances
 Substitution de Liskov
 Responsabilité unique
 Ségrégation des interfaces
48
Réponse
 Ouvert/fermé
 Dans ce cas, vous n'étendez pas la classe, mais vous modifiez
le code d'origine. Selon le principe ouvert/fermé, une classe
devrait être ouverte pour l'extension, mais fermée à la
modification. Vous enfreignez ce principe ici.

49
Question 2
 Une classe dérivée implémente une méthode
redéfinie en lançant une
UnsupportedOperationException. Quel principe de
conception SOLID est enfreint dans cette situation ?
 Ouvert/fermé
 Inversion des dépendances
 Substitution de Liskov
 Responsabilité unique
 Ségrégation des interfaces

50
Réponse
 Substitution de Liskov
 Dans cette situation, vous êtes confronté à un problème
d'héritage. Votre implémentation de bas niveau ne se conforme
pas à celles de haut niveau. Il s'agit d'une violation de la
substitution de Liskov.

51
Question 3
 Si une méthode d'une classe présente de trop
nombreux cas d’exécution possibles, elle est difficile
à tester. Quel principe de conception SOLID est
enfreint dans cette situation ?
 Ouvert/fermé
 Inversion des dépendances
 Substitution de Liskov
 Responsabilité unique
 Ségrégation des interfaces

52
Réponse
 Responsabilité unique
 Si une méthode a de nombreuses options, cela signifie qu'elle
effectue plusieurs choses, et ainsi qu'elle a plusieurs
responsabilités. Cela enfreint le principe de responsabilité
unique.

53
Question 4
 Quel est le principe SOLID enfreint par l'extrait de code
suivant ?

 Ouvert/fermé
 Inversion des dépendances
 Substitution de Liskov
 Responsabilité unique
 Ségrégation des interfaces
54
Réponse
 Ségrégation des interfaces
 L'interface présente plusieurs responsabilités, ce qui enfreint le
principe de ségrégation des interfaces. Et puisque le principe
de responsabilité unique concerne les classes, cela ne peut
pas être la bonne réponse.

55
Question 5
 Quel extrait de code complète le code suivant et lui permet de se conformer
au principe d'inversion des dépendances ?

(a) (b) (c) (d)

56
Réponse
 ❌ Réponse 1 : l'élément Motorcycle a introduit un nouvel ensemble de
méthodes. Tout élément qui l'utilisera devra changer ses méthodes afin
d'appeler ces nouvelles fonctions. Ici, la classe de bas niveau pilote la classe
de haut niveau.
 ❌ Réponse 2 : l'élément SelfDrivingCar dispose des mêmes méthodes, mais
n'a pas inclus « implements Driveable », de sorte qu'il ne peut pas être
remplacé par un élément Driveable.
 ✅ Réponse 3 : une classe de haut niveau (Driver) utilise l'interface fournie
(Driveable).
 ❌ Réponse 4 : la classe Driver doit appeler des méthodes, pour chaque
véhicule, en fonction de la nature de ce dernier. Il n'utilise pas du tout
d'interface pour interagir. 57
Question 6
 Lequel des extraits de code suivants complète correctement le principe
ouvert/fermé de l'extrait de code suivant ?

58
Réponse
 ❌ Réponse 1 : aucune des classes n'implémente l'interface
Rollable, dont a besoin la classe Game.
 ✅ Réponse 2
 ❌ Réponse 3 : la classe Game a été modifiée de façon à
seulement utiliser des objets Die. Elle n'est pas ouverte à la
modification si vous utilisez un mécanisme différent.
 ❌ Réponse 4 : une classe entièrement nouvelle, MyGame, a été
introduite pour gérer une implémentation différente. Elle n'est
ouverte à rien.
59
Exercice
 Consider the following partial design of an application.

 Can you identify cases where SOLID design principles is


applied/violated?
60
Correction
 Single Responsibility Principle
 There seems to be a good separation and isolation of responsibilities between the CoffeeApp and
different types of CoffeeMachine.
 Open/Closed Principle
 Adding new types of coffee machines will not require any modification to existing ones. (I.e. app
is open for extension but closed for modification.)
 Liskov Substitution Principle
 There is not enough information to consider the application of this principle. Based on the diagram,
it seems every subtype is substitutable for their parent type.
 Interface Segregation Principle
 The responsibilities of CoffeeMachine is broken down into FilterCoffeeMachine and EspressoCoffeeMachine (more
granular interfaces to prevent a bloated CoffeeMachine interface).
 Dependency Inversion Principle
 High-level classes (e.g. CoffeeApp) does not depend on low-level classes (e.g. BasicCoffeeMachine); instead
they both depend on abstractions (i.e. CoffeeMachine interface).
61

Vous aimerez peut-être aussi