Vous êtes sur la page 1sur 15

Bonnes pratiques objet en .

net :
Introduction aux principes SOLID
par Philippe Vialatte (philippe.developpez.com)
Date de publication : 21 Octobre 2008
Dernire mise jour :
Dans cet article, je vais essayer de vous prsenter les principes SOLID, tels que dcrits
dans le livre de Robert Martin, Agile Software Development, Principles, Patterns,
and Practices. On va essayer de voir l'intrt de ces principes, et comment les appliquer,
de faon (si possible) abordable par tout le monde.
Commentez cet article :
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 2 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
I - Introduction..............................................................................................................................................................3
I - A - Cohsion......................................................................................................................................................3
I - B - Couplage..................................................................................................................................................... 3
I - C - Encapsulation.............................................................................................................................................. 3
II - Responsabilit unique (SRP: Single Responsibility Principle)...............................................................................4
II - A - Dfinition.....................................................................................................................................................4
II - B - Comment l'appliquer...................................................................................................................................4
II - B.1 - Analyse et regroupement des mthodes........................................................................................... 4
II - B.2 - Analyse du code................................................................................................................................ 4
II - C - Exemple......................................................................................................................................................4
III - Ouvert/ferm (OCP: Open/closed Principle).........................................................................................................7
III - A - Dfinition....................................................................................................................................................7
III - B - Comment l'appliquer..................................................................................................................................7
III - C - Exemple.....................................................................................................................................................8
IV - Substitution de Liskov (LSP: Liskov Substitution Principle)................................................................................. 9
IV - A - Dfinition................................................................................................................................................... 9
IV - B - Comment l'appliquer............................................................................................................................... 10
IV - C - Exemple.................................................................................................................................................. 10
V - Sparation des Interfaces (ISP: Interface Segregation Principle)....................................................................... 11
V - A - Dfinition.................................................................................................................................................. 11
V - B - Comment l'appliquer................................................................................................................................ 12
V - C - Exemple................................................................................................................................................... 12
VI - Inversion des dpendances (DIP: Dependency Inversion Principle)................................................................. 12
VI - A - Dfinition................................................................................................................................................. 12
VI - B - Comment l'appliquer............................................................................................................................... 13
VI - C - Exemple.................................................................................................................................................. 13
VII - Conclusion......................................................................................................................................................... 14
VIII - Remerciements................................................................................................................................................. 15
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 3 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
I - Introduction
Aujourd'hui, une majorit des dveloppeurs dveloppe avec des langages orients objet.
L'objet est partout, dans tous (ou presque) les langages, et tous les dveloppeurs comprennent intimement ce qu'on
entend par de la programmation oriente objet...ou pas.
Aprs avoir pass de nombreuses annes maintenir et dvelopper du code, on se rend malheureusement compte
que les principes du dveloppement objet sont malheureusement soit ignors, soit mal compris par de nombreux
dveloppeurs, ce qui rend assez souvent la maintenance des logiciels au mieux malaise, au pire impossible.
SOLID est l'acronyme de cinq principes de base (Single Responsibility Principle, Open/Closed Principle, Liskov
Substitution Principle, Interface Segregation Principle et Dependency Inversion Principle) que l'on peut appliquer au
dveloppement objet.
On verra dans cet article que ce sont avant tout des principes de bon sens. Aucun ne ncessite une connaissance
approfondie d'un langage donn. Pour des raisons de got, les exemples seront donns en C#. Ces principes,
lorsqu'ils sont compris et suivis, permettent d'amliorer la cohsion, de diminuer le couplage, et de favoriser
l'encapsulation d'un programme orient objet.
Avant d'attaquer les principes en eux-mmes, on va brivement revoir quoi correspondent ces mtriques.
I - A - Cohsion
La cohsion traduit quel point les pices d'un seul composant sont en relation les unes avec les autres. Un module
est cohsif lorsqu'au haut niveau d'abstraction il ne fait qu'une seule et prcise tche. Plus un module est centr sur
un seul but, plus il est cohsif.
I - B - Couplage
Le couplage est une mtrique qui mesure l'interconnexion des modules. Deux modules sont dit coupls si une
modification d'un de ces modules demande une modification dans l'autre.
I - C - Encapsulation
L'ide derrire l'encapsulation est d'intgrer un objet tous les lments ncessaires son fonctionnement, que ce
soit des fonctions ou des donnes.
Le corolaire est qu'un objet devrait (et non pas doit, comme c'est souvent expliqu) masquer la cuisine interne de la
classe, pour exposer une interface propre, de faon ce que ses clients puissent manipuler l'objet et ses donnes
sans avoir connaitre le fonctionnement interne de l'objet.
Pour plus de clart, on verra en quoi chaque principe va jouer sur ces mtriques.
Comme l'indique le titre de cet article, c'est un article d'introduction. En consquence,
j'ai essay de rester un niveau abordable, ni trop thorique, ni trop pratique. Certaines
formulations sont simplifies, et les exemples restent volontairement un niveau
"scolaire".
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 4 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
II - Responsabilit unique (SRP: Single Responsibility Principle)
II - A - Dfinition
Ce principe est le plus vieux de ceux tudis ici. Il se base sur les travaux de Tom DeMarco, en 1979, qui le qualifie
de principe de cohsion. La dfinition que l'on va admettre est:
"Si une classe a plus d'une responsabilit, alors ces responsabilits deviennent couples.
Des modifications apportes l'une des responsabilits peuvent porter atteinte ou
inhiber la capacit de la classe de remplir les autres. Ce genre de couplage amne
des architectures fragiles qui dysfonctionnent de faon inattendues lorsqu'elles sont
modifies." -- Robert C. Martin
Le principe de responsabilit unique, rduit sa plus simple expression, est qu'une classe donne ne doit avoir
qu'une seule responsabilit, et, par consquent, qu'elle ne doit avoir qu'une seule raison de changer.
Les avantages de cette approche sont les suivants:
Diminution de la complexit du code
Augmentation de la lisibilit de la classe
Meilleure encapsulation, et meilleure cohsion, les responsabilits tant regroupes
II - B - Comment l'appliquer
Bien que ce principe s'nonce assez facilement, c'est assez souvent le plus compliqu mettre en oeuvre. En effet,
on a souvent tendance donner trop de responsabilits un objet, et on a parfois du mal identifier, sur un objet
existant, les responsabilits qui lui choient.
Une responsabilit peut tre identifie, dans un code existant, des faons suivantes:
II - B.1 - Analyse et regroupement des mthodes
Pour une classe de taille importante, il est souvent bnfique de lister toutes les mthodes, et de regrouper celles
dont le nom ou les actions semblent tre de la mme famille. Si plusieurs groupes apparaissent dans une classe,
c'est un bon indicateur que la classe doit tre reprise.
II - B.2 - Analyse du code
Une autre mthode est de regarder les dpendances externes de la classe.
La mthode appelle-t-elle directement la base de donnes ? Utilise-t'elle une API spcifique ? Certains membres
sont-ils appels uniquement par une fonction, ou par un sous-ensemble de fonctions ?
Si c'est le cas, ce sont peut-tre des responsabilits annexes, dont il faut se dbarrasser...
II - C - Exemple
Pour faire simple, on va prendre un mauvais exemple, que l'on va refactoriser.
On va se baser sur le scnario suivant : notre socit veut concevoir un nouveau systme de suivi de bugs. Pour
cela, on va implmenter un systme de suivi de Work Items. Aprs un premier jet, on va obtenir le code suivant:
public class WorkItem{

private string _id;
private string _name;
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 5 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/

// accesseurs...pas la peine de s'appesantir...
public void Save(){
SqlConnection cnx = new SqlConnection(ConfigurationManager.ConnectionStrings["database"]);
cnx.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = cnx;
cmd.CommandText = "INSERT INTO WorkItem (Id, Name) VALUES ('";
cmd.CommandText += _id + "','" + _name + "')";
cmd.ExecuteNonQuery();
cnx.Close();
}

public void GetById(string id){
SqlConnection cnx = new SqlConnection(ConfigurationManager.ConnectionStrings["database"]);
cnx.Open();

SqlCommand cmd = new SqlCommand();
cmd.Connection = cnx;
cmd.CommandText = "SELECT Id, Name FROM WorkItem where Id = '" + id + "'";
SqlDataReader dr = command.ExecuteReader();

if (dr.Read()){
_id = dr["id"].ToString();
_name = dr["name"].ToString();
}else{
return null;
}
}
}
Ce fonctionnement correspond plus ou moins un pattern d'architecture, le pattern
ActiveRecord. Il n'est pas mauvais en soi, mais dans le cadre du principe SRP, pose
quelques problmes. A chacun de peser le pour et le contre de chaque approche...
En termes de responsabilits, cette classe a les responsabilits
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 6 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
de crer les objets
de stocker les donnes de l'objet
et de grer la persistance des objets.
(On va passer sur le couplage fort entre la structure de la base et de l'objet, sur les risques d'injection, sur la rptition
du code pour grer les connexions, etc...)
Apres refactorisation, on va obtenir trois objets, une Factory, un gestionnaire d'accs aux donnes, et un objet de
transport de donnes.
public class WorkItem{

private string _id;
private string _name;
// accesseurs...pas la peine de s'appesantir...

public WorkItem(DataRow dr){

_id = dr["id"].ToString();
_name = dr["name"].ToString();
}
}
public class WorkItemFactory{

public void Save(WorkItem item){
WorkItemDataAccess.Save(item.Id, item.Name);
}
public WorkItem GetWorkItemById(string id){
Datarow dr = WorkItemDataAccess.GetById(id);
if (dr == null){
return null;
}
return new WorkItem(dr);
}
}

public static class WorkItemDataAccess{

public static void Save(string id, string name){
/// requetes sql
}
public static DataRow GetById(string id){
SqlConnection cnx = new
SqlConnection(ConfigurationManager.ConnectionStrings["database"]);
cnx.Open();

SqlCommand cmd = new SqlCommand();
cmd.Connection = cnx;
cmd.CommandText = "SELECT Id, Name FROM WorkItem where Id = '" + id + "'";

DataTable dt = new DataTable();
using (SqlDataAdapter da = new SqlDataAdapter(cmd)) {
da.Fill(dt);
}

return dt.Rows.Count == 0 ? null : dt.Rows[0];
}
}

Suite a cette factorisation, les responsabilits de nos trois classes sont beaucoup plus videntes, la classe d'accs
aux donnes ne traite plus que des donnes, l'objet possde des mthodes pour manipuler ses propres donnes, et
la factory a la responsabilit de faire travailler ensemble la classe d'accs aux donnes et l'objet...
La partie mtier est un peu mince, mais on va essayer de l'toffer plus tard.
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 7 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
Une notion garder l'esprit est qu'il ne faut pas aller trop loin dans la sparation des responsabilits, au risque de
tomber dans un excs inverse et se retrouver avec un domaine anmique.
Un refactoring supplmentaire (et valide) pourrait faire intervenir un nouvel objet CommonDataAccess, qui serait
appel par les classes d'accs aux donnes
III - Ouvert/ferm (OCP: Open/closed Principle)
III - A - Dfinition
Le principe Ouvert/ferm est, tout comme la substitution de Liskov, issu d'un article datant d'une vingtaine d'annes
(1988). Sa formulation initiale est la suivante:
Les entits logicielles (classes, modules, fonctions, etc.) doivent tre ouvertes pour
l'extension, mais fermes la modification. --Bertrand Meyer
L'ide originale de Meyer tait qu'en fait, une classe logicielle tait un package de donnes, qui ne devait, une fois
implment, ne plus tre modifie que pour corriger une erreur. Toute nouvelle fonctionnalit ne devrait, selon lui,
pouvoir tre rajout qu'en ajoutant une nouvelle classe, laquelle pouvait ventuellement hriter de la classe d'origine.
De nos jours, lorsque l'on parle de ce principe, le sens que l'on lui donne en gnral est celui repris dans un article
de 1996 par Robert Martin. La dfinition tendue qui en est donne est la suivante:
"Les modules qui se conforment au principe ouvert/ferme ont deux attributs principaux.
1 - 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 faons nouvelles et diffrentes
si les exigences de l'application sont modifies, ou pour remplir les besoins d'une autre
application.
2 - Ils sont "Ferms la modification". Le code source d'un tel module ne peut pas tre
modifi. Personne n'est autoris y apporter des modifications."
--Robert C. Martin
Le but de ce principe est donc de tendre, non plus vers des objets immuables, mais vers des objets auxquels les
clients pourront ajouter de nouveaux comportements sans en modifier la mcanique interne.
La diffrence fondamentale entre les deux approches est que, dans un cas, on va utiliser un hritage
d'implmentation, alors que dans l'autre, on va se baser sur des contrats.
Pour quelles raisons voudrait-on pouvoir mettre notre programme en conformit avec ce principe ?
Plus de flexibilit par rapport aux volutions
Diminution du couplage
III - B - Comment l'appliquer
Deux possibilits nous sont donnes, la premire tant de chercher identifier une fonction ou une mthode dont
le comportement est susceptible d'tre fortement impact par une modification ultrieure. Malheureusement, on va
plus souvent qu' notre tour tre surpris par les demandes de nos clients. La seconde mthode, plus agile, est de
conserver un design simple, et, lorsque l'on arrive aux limites de ce design, d'en changer...
On va donc, le plus souvent, commencer par le code le plus simple pouvant fonctionner, et, lorsque l'on rencontre
une exception nous obligeant modifier la classe pour l'tendre, s'assurer qu'une modification ultrieure de mme
type ne nous forcera pas modifier de nouveau notre design. Evidemment, avec l'exprience, on est souvent plus
mme de sentir quelles portions du programme sont plus mme d'tre modifies, mais c'est un pari que l'on fait.
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 8 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
Comme rgles de bonne conduite, on peut essayer d'une part de ne pas dpendre du type d'un objet pour choisir
un chemin de traitement. Cela se rapproche du principe LSP, que l'on va voir plus bas. D'autre part, on peut limiter
l'hritage, en y prfrant la composition.
Si vous vous intressez un petit peu aux design patterns, vous devez certainement avoir dj t expos ce
principe...
En effet, un certain nombre de design patterns sont une mise en pratique de ce principe. Par exemple, le DP
Dcorateur permet de rajouter un objet de nouvelles fonctionnalits sans modifier son code, par dcoration, le
DP Visiteur permet d'tendre les capacits de la collection parcourue sans modifier l'implmentation des objets
manipuls, etc...
III - C - Exemple
On va reprendre notre exemple prcdent de Work Item.
Aprs une itration, on va avoir une nouvelle demande de notre client (interne, mais client quand mme), savoir qu'il
veut pouvoir grer plusieurs types de work items. En effet, certains vont matrialiser des tches pour le dpartement
informatique, d'autres, des tches pour le dpartement finance, marketing, ou gestion. Notre premier reflexe va tre
le suivant:
public class WorkItem{
private string _id;
private string _name;
}
public class ITWorkItem : WorkItem{
public void ManageITWorkItem(){
// execute des actions relatives au departement informatique
}
}
public class FinanceWorkItem : WorkItem{
public void ManageFinanceWorkItem(){
// execute des actions relatives au departement finances
}
}
public class MarketingWorkItem : WorkItem{
public void ManageMarketingWorkItem(){
// execute des actions relatives au departement marketing
}
}
Tout cela semble marcher. Seulement, que se passe-t-il lorsque l'on veut traiter un ensemble de Work Items ?
public RunAllWorkItems(List<WorkItems> items){

foreach(WorkItem item in items){

if (item is ITWorkItem){
item.ManageITWorkItem();
}else if(item is FinanceWorkItem){
item.ManageFinanceWorkItem();
}else if(item is MarketingWorkItem){
item.ManageMarketingWorkItem();
}
}
}
On se retrouve avec une implmentation fragile, qui va ncessiter, caque cration d'un nouveau type de Work Item,
la modification de la fonction RunAllWorkItems, et de toutes les fonctions qui se basent sur le type des Work Items.
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 9 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
Une solution conforme OCP serait d'ajouter une interface IWorkItem, et de lui ajouter une fonction ManageWorkItem,
ManageWorkItem devenant le contrat que chaque classe implmentant IWorkItem devra remplir.
public interface IWorkItem {
void ManageWorkItem();
}
public class WorkItem {
private string _id;
private string _name;
//on saute les accesseurs...
}
public class ITWorkItem : WorkItem, IWorkItem{
public void ManageWorkItem(){
// execute des actions relatives au departement informatique
}
}
public class FinanceWorkItem : WorkItem, IWorkItem{
public void ManageWorkItem(){
// execute des actions relatives au departement finances
}
}
public class MarketingWorkItem : WorkItem, IWorkItem{
public void ManageWorkItem(){
// execute des actions relatives au departement marketing
}
}
......
public RunAllWorkItems(List<IWorkItems> items){

foreach(IWorkItem item in items){
item.ManageWorkItem();
}
}
De cette faon, on pourra ajouter de nouveaux types de comportement (de nouveaux Work Items) sans avoir
modifier la fonction RunAllWorkItems. On peut donc l'tendre sans avoir la modifier.
IV - Substitution de Liskov (LSP: Liskov Substitution Principle)
IV - A - Dfinition
La substitution de Liskov, telle que dfinie par Barbara Liskov et Jeannette Wing, s'nonce ainsi :
Ce que l'on veut est vrifier le proprit de substitution suivante:
Si pour chaque objet o1 de type S il existe un objet o2 de type T tel que pour tout
programme P dfini en termes de T, le comportement de P est inchang quand on
substitue o1 o2, alors S est un sous-type de T
A cette dfinition fonctionnelle lgrement alambique, je prfre, une fois de plus, la dfinition simplifie donne
par Robert Martin:
Les sous-types doivent tre remplaables par leur type de base.
Plus simple, non ;) ?
La, je vais en voir un ou deux (ou plus) dire: "Oui, mais partir du moment o ma classe S hrite de ma classe T",
je dois pouvoir caster S en T et l a va marcher...
Justement, non...
Le but de ce principe est exactement de pouvoir utiliser une mthode sans que cette mthode ait connaitre la
hirarchie des classes utilises dans l'application, ce qui veut dire:
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 10 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
pas de cast
pas de as
pas de is
et surtout pas d'introspection/rflexion
En effet, si on vrifie le type des objets, on va violer non seulement LSP, mais aussi OCP, il suffit de voir l'exemple
prcdent de violation d'OCP, qui tait une violation flagrante de LSP. L'inverse ne se vrifie pas, on peut tout fait
ne pas respecter OCP (l'exemple canonique tant de vouloir ajouter une routine de tri nos objets, ce qui demande
gnralement de modifier les classes...), et rester en conformit avec LSP.
Ce principe apporte:
Augmentation de l'encapsulation
Diminution du couplage. En effet, LSP permet de contrler le couplage entre les descendants d'une classe et
les clients de cette classe.
IV - B - Comment l'appliquer
Pour dtecter le non respect de ce principe, on va se poser la question de savoir si on peut, sans dommage, remplacer
la classe en cours par une interface d'un niveau suprieur.
Le problme auquel on va se heurter est que, pour valider que le principe LSP est respect, il faut le valider pout
tous les clients de notre arborescence de classe. L'exemple canonique est le suivant: Si une classe Carr hrite de
Rectangle, un client manipulant exclusivement des rectangles risque de vouloir affecter une largeur et une hauteur
diffrentes un carr. Soit le carr sera inconsistant (hauteur et largeur diffrentes), soit la fonction de surface sera
inconsistante (le client attendant une surface de H*L, et recevant H*H ou L*L).
Ce principe nous invite revoir la notion d'hritage, dans le sens ou il dfinit la notion d'hritage comme celui du
comportement qu'un client peut attendre d'un objet. Dans ce cas, on voit qu'en termes de dveloppement objet, un
carr n'est pas un rectangle, car leurs comportements diffrent pour un client donn.
IV - C - Exemple
En dehors de l'utilisation d'un dterminant de type (is, as, rflexion), on va violer ce principe si un descendant modifie le
comportement de la classe parente de faon a ce que le seul moyen, pour le programme, de fonctionner correctement,
est de connaitre le type concret de l'objet.
Pour continuer sur notre lance, on va redfinir la mthode ToString de nos Work Items.
Pour cela, on va dfinir, au niveau de notre classe WorkItem, une fonction Tostring, qui va renvoyer l'id et le nom
d'un work item.
public class WorkItem {
private string _id;
private string _name;
//on saute les accesseurs...
public override string ToString(){
// renvoie
return "id : " + _id + " - name : " + _name;
}
}
Une violation du principe LSP serait d'avoir, au niveau des FinanceWorkItem, par exemple, un override de ToString
dans ce gout-la:
public override string ToString(){
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 11 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
throw new Exception("Pas de ToString, Merci !");
}
(Je sais, c'est moyennement utile, mais c'est pour l'exemple).
Inversement, mon avis, LSP ne doit pas forcer les descendants d'une classe conserver le mme comportement
mtier.
Par exemple, si dans mes FinanceWorkItem, je fais:
public override string ToString(){
return "id : " + _id + " name : " + _name;
}
Et qu'un de mes clients utilise une fonction de manipulation de chaine de caractres pour rcuprer le nom du Work
Item, comme :
string name = workItem.ToString().Split('-')[1].Substring(8);
La nouvelle version provoquera une exception chez le client, mais ne sera pas une violation de LSP, le comportement
restant le mme (on retourne une reprsentation de l'objet sous forme de chane), mais le contenu retourn change
pour correspondre a une implmentation spcifique.
V - Sparation des Interfaces (ISP: Interface Segregation Principle)
V - A - Dfinition
Pour une fois, ce principe se passe d'une dfinition formelle, ce qui y ressemble le plus tant la dfinition suivante:
Les clients d'une entit logicielle ne doivent pas avoir dpendre d'une interface qu'ils
n'utilisent pas.
Le premier effet d'avoir une interface trop complique va se ressentir assez vite. En effet, toute classe implmentant
une interface doit implmenter chacune de ses fonctions. On va donc trs vite se retrouver avec une confusion sur
le rle des sous classes.
Dans le framework .net (on pourrait certainement trouver des exemples en Java et C++), de nombreuses classes se
conforment ce principe. Par exemple, la classe gnrique List<T> va implmenter toutes les interfaces suivantes:
IList<T>
ICollection<T>
IEnumerable<T>
IList
ICollection
IEnumerable
Les risques d'avoir des interfaces trop compliques ne sont pas toujours vidents. En fait, ils reviennent au poids
que l'on compte mettre sur les clients de l'interface. En effet, de faon gnrale, plus une interface est complique,
plus l'abstraction qu'elle prsente est vaste, plus elle est susceptible d'voluer avec le temps. Chaque volution de
l'interface va entrainer une livraison ou une modification de l'ensemble des clients.
En consquence, si l'interface doit changer pour un client A qui utilise la fonction A, cela va aussi impacter le client
B, qui n'utilise pas la fonction A, mais qui dpends de la mme interface
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 12 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
Ce principe apporte principalement une diminution du couplage entre les classes (les classes ne dpendant plus les
unes des autres). L'autre avantage d'ISP est que les clients augmentent en robustesse.
V - B - Comment l'appliquer
Appliquer ISP est assez simple dans la thorie. En fait, on va revenir plus ou moins la mme dmarche que pour
SRP. On va runir les groupes "fonctionnels" des mthodes de la classe dans des Interfaces spares. L'ide tant
de favoriser le dcoupage de faon ce que des clients se conformant SRP n'aient pas dpendre de plusieurs
interfaces.
V - C - Exemple
Dans nos exemples de Work Items, on va devoir grer des Work Items pour lesquels il existe une deadline. Nos Work
Items dpendant tous de IWorkItem, on va directement ajouter Les informations de gestion de deadline au niveau
de IWorkItem et de WorkItem.
Apres un petit brainstorming, on fait une mise jour de nos classes, et on obtient le code suivant:
public interface IWorkItem {
...
bool IsDeadLineExceeded();
}
public class WorkItem {
private DateTime _deadLine;
//on saute les accesseurs...
}
Jusqu'ici, tout va bien...Sauf que le marketing ne veut pas entendre parler de deadline pour ses items. On peut donc,
soit renvoyer une information errone, pour continuer utiliser le IWorkItem courant, soit se conformer au principe
ISP, et sparer notre interface en IWorkItem et IDeadLineDependent.
public interface IWorkItem {
void ManageWorkItem();
string ToString();
}
public interface IDeadLineDependent {
bool IsDeadLineExceeded();
}
L'intrt est que, si demain on a besoin d'une fonction ExtendDeadline dans IDeadLinedItem, cela n'impactera pas
les WorkItems ne comportant pas de Deadline. Et si on ne le modifie pas, on n'introduit pas de bugs.
VI - Inversion des dpendances (DIP: Dependency Inversion Principle)
VI - A - Dfinition
Le principe d'inversion des dpendances est un peu un principe secondaire. En effet, il rsulte d'une application
stricte de deux autres principes, savoir les principes OCP et LSP. Sa dfinition est la suivante:
Les modules de haut niveau ne doivent pas dpendre des modules de bas niveau. Les
deux doivent dpendre d'abstractions.
Les abstractions ne doivent pas dpendre des dtails. Les dtails doivent dpendre des
abstractions.
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 13 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
Par module de haut niveau, on va entendre les modules contenant les fonctionnalits mtier, les modules de bas
niveau grant la communication entre machines, les logs, la persistance. Si on change le mode de fonctionnement
de la base (passage de Oracle SQL Server), du rseau (changement de protocole), de systme d'exploitation, les
classes mtiers ne doivent pas tre impactes. Inversement, le fait de changer les rgles de validation au niveau de
la partie mtier du framework ne doit pas demander une modification de la base de donnes ( la limite, modifier une
fonction, mais ne pas changer les briques de base).
Ce principe apporte:
Une nette diminution du couplage
Une meilleure encapsulation, l'implmentation concrte pouvant ventuellement tre choisie dynamiquement
Ce principe est quivalent au principe d'Hollywood ("Ne nous appelez pas, nous vous
appellerons"), qui est une forme plus gnrale d'inversion de contrle.
Pour plus d'information sur ce principe, rendez-vous ici : (Wikipedia) : Inversion de
contrle
VI - B - Comment l'appliquer
L'ide est que chaque point de contact entre deux modules soit matrialis par une abstraction.
Par abstraction, on va entendre gnralement, une interface, mais on peut aussi considrer qu'une classe (une
factory, par exemple) reprsente une abstraction d'une classe de niveau plus lev ou plus bas.
VI - C - Exemple
On va reprendre notre couche d'accs aux donnes, et la factory qui la manipule. Actuellement, les fonctions de
chargement et de sauvegarde des objets mtier possdent une dpendance forte sur la couche d'accs aux donnes.
On a donc un diagramme de classe quivalent a:
Par consquent, on va avoir, dans l'tat, du mal isoler nos fonctions de cration d'objets de notre couche de donnes.
Donc, on va avoir du mal les tester.
Pour se conformer au principe DIP, on va modifier notre code de cette faon:
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 14 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
Pour aller encore plus prs du principe DIP, on pourrait ajouter encore une couche d'abstraction supplmentaire, en
retournant des interfaces aux couches hautes.
Une fois notre code factoris, va se poser la question de comment affecter la bonne implmentation de nos interfaces
au bon composant.
Une premire approche est de passer notre client une classe service choisie, ainsi qu'une rfrence sur un objet
d'accs aux donnes. Cette approche va, en fait, dporter le choix de l'implmentation au niveau de la construction
du client. Elle peut, mon avis, tre utilise pour des projets de petite taille. Pour des projets plus consquents,
une seconde option est d'utiliser un configurateur externe, qui va, durant l'excution de notre code, renvoyer la
bonne implmentation concrte de notre abstraction. Un certain nombre d'outils d'injection de dpendance sont
disponibles et matures, je vous renvoie Google pour plus d'information sur ces outils. Vous trouverez, par exemple,
StructureMap, Castle Windsor, ou, chez Microsoft, Unity.
L'idal est de chercher tendre vers ce principe, pas forcement de l'appliquer la lettre, ce qui peut se montrer
contre-productif.
En effet, pour se conformer 100% ce principe, il faudrait que:
aucune variable ne rfrence une classe concrte
aucune classe ne drive d'une classe concrte
aucune classe ne rcrive une mthode d'une de ses classes de base.
VII - Conclusion
Ces principes peuvent s'noncer clairement, les utiliser demande de les conserver l'esprit durant chaque session
de dveloppement, et le cot initial d'introduction peut tre dcourageant pour certains projets.
Pour cette raison, ils sont souvent utiliss conjointement avec une mthodologie Agile, qu'elle soit XP, Scrum ou
autre, supporte en tout cas par une panoplie de tests unitaires. En conclusion, le choix de se conformer ou non
ces principes doit donc se faire en fonction du contexte du projet, et de l'importance que l'on donne la qualit et
l'volutivit du programme dvelopp. Il est nanmoins toujours utile d'en avoir au moins entendu parler :).
Pour un approfondissement avec des gens beaucoup plus intelligents que moi, je vous
invite vous rendre sur les forums de la communaut Alt.Net, o j'ai, pour la premire
fois, entendu parler des principes SOLID et de lire le livre de Robert Martin.
Bonnes pratiques objet en .net : Introduction aux principes SOLID par Philippe Vialatte (philippe.developpez.com)
- 15 -
Copyright 2008 - Philippe Vialatte. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de dommages et intrts. Droits de diffusion permanents accords developpez LLC.
http://philippe.developpez.com/articles/SOLIDdotNet/
VIII - Remerciements
Merci tomlev pour sa premire relecture (sans laquelle l'article serait srement moins intressant), et Skalp pour
ses corrections