Académique Documents
Professionnel Documents
Culture Documents
Architecture des SI I
SOMMAIRE
Principe de responsabilité
responsabil unique (SRP)
Qu'est-ce
ce que le principe de responsabilité unique ?
Le principe de responsabilité unique stipule que les objets ne devraient avoir qu'une seule
responsabilité et qu'ils ne devraient avoir qu'une
qu' seule raison de changer.
En d'autres termes,
es, vous devez concevoir le logiciel de manière à ce que chaque classe soit liée à une
seule responsabilité. Cela ne signifie pas que votre classe ne doit contenir qu'une seule méthode ou
propriété, vous pouvez avoir plusieurs membres (méthodes ou propriétés)
propriétés) tant qu'ils sont liés à une
seule responsabilité ou fonctionnalité. Ainsi, avec l'aide de SRP, les classes deviennent plus petites et
plus propres et donc plus faciles à maintenir.
Exemple qui viole le principe de responsabilité unique ?
Comme vous pouvez vez le voir dans le code ci-dessous,
ci dessous, nous avons créé la classe ServiceFilm avec
quatre fonctionnalités telles que l'ajout et la suppression d’un film, la journalisation des erreurs ainsi
que l'envoi d'e-mails.
mails. Nous avons donc ajouté trop de responsabilités à la classe ServiceFilm et nous
avons violé le principe de responsabilité unique.
En effet, l'envoi d'e-mails
mails et la journalisation des erreurs ne font pas partie de la classe ServiceFilm. Il
devient alors très difficile de changer une responsabilité sans briser les autres responsabilités. Ainsi,
vous pouvez avoir plusieurs raisons de modifier la classe, non seulement pour changer la façon dont
les films sont gérés, mais en plus, vous pouvez également avoir des raisons supplémentaires de
changer la classe lorsque la logique de la journalisation des erreurs change ou bien la logique d’envoi
des e-mails change.
public class ServiceFilm
{
private readonly List<string
string> films = new List<string>();
private static int count;
public void AddFilm(string
string text)
{
try
{
films.Add($"{++count}
{++count}: {text}");
MailMessage mailMessage = new MailMessage("EMailFrom", "EMailTo",, "EMailSubject",
"EMailBody");
this.SendFilmEmail(mailMessage);
.SendFilmEmail(mailMessage);
}
catch (Exception ex)
{
System.IO.File.WriteAllText(
System.IO.File.WriteAllText(@"c:\ErrorLog.txt", ex.ToString());
}
}
public void DeleteFilm(int
int index)
{
try
{ films.RemoveAt(index); }
catch (Exception ex)
{
System.IO.File.WriteAllText(
System.IO.File.WriteAllText(@"c:\ErrorLog.txt", ex.ToString());
}
}
public void SendFilmEmail(MailMessage mailMessage)
{
try
{
// Here we need to write the Code for Email setting and sending the Film mail
}
catch (Exception ex)
{
System.IO.File.WriteAllText(
System.IO.File.WriteAllText(@"c:\ErrorLog.txt", ex.ToString());
}
}
}
En mélangeant plusieurs responsabilités dans une seule classe, nous obtenons les inconvénients
suivants,
● Code difficile à comprendre,
● Code difficile à tester,
● Risque de dupliquer la logique d'autres parties de l'application.
Pour respecter le principe SRP, vous devez isoler les responsabilités. Pour le faire, vous auriez besoin
de définir une classe ou une interface pour chaque responsabilité. Lorsque vous concevez vos classes,
c
le nom de la classe doit désigner sa responsabilité.
Dans notre exemple, nous allons isoler les quatre responsabilités en créant pour chaque
responsabilité une classe. Dans la classe de ServiceFilm,, seules les fonctionnalités liées au Film seront
implémentées. La classe Logger ne sera utilisée qu'à des fins de journalisation. De même, la classe
Email va gérer les activités de messagerie.
public class ServiceFilmSPR
{
private readonly List<string
string> films = new List<string>();
private readonly Logger logger=new
logger= Logger();
private readonly MailSender emailSender = new MailSender();
private static int count = 0;
Principe ouvert-fermé
fermé (OCP)
ce que le principe ouvert fermé ?
Qu’est-ce
Le principe ouvert-fermé
fermé stipule que les entités logicielles telles que les modules, les classes, les
fonctions, etc. doivent être ouverte à l’extension mais fermée à la modification..
Ce principe suggère que nous devons concevoir une entité logicielle de telle sorte qu'elle permet
d'étendre son comportement sans modifier son code courant, lorsque de nouvelles exigences
surviennent et doit être misent en œuvre. La conformité à ce principe facilite la création
d’applications réutilisables et peuvent être maintenues facilement.
Pour réaliser ce principe, vous avez besoin d’utiliser des abstractions, qui est la clé, et de permettre
aux consommateurs d’accéder aux classes à travers ces abstractions définies. Les dérivés qui sont
créés à partir de ces abstractions sont clos pour modification depuis l'abstraction est fixé. Cependant,
vous pouvez étendre le comportement en créant de nouveaux dérivés de l'abstraction qui qu a déjà été
défini.
Exemple qui viole le principe ouvert fermé
Nous
us pouvons appliquer OCP en utilisant une interface, une classe abstraite, une classe générique,
des méthodes abstraites, des méthodes virtuelles et des méthodes d’extension lorsque vous
souhaitez étendre les fonctionnalités, selon le besoin.
Dans l'exemple ci-dessus,
dessus, nous créons une interface IServiceFilm contenant la méthode
GetFilmDiscount.. Pour chaque genre de film, nous lui créons une classe qui hérite de l’interface
IServiceFilm.
Lorsqu’un nouveau genre de film doit être ajouté, nous devons ajouter une autre classe qui hérite de
l’interface IServiceFilm.. En conséquence, l’entité parent servira de classe de base abstraite qui peut
être réutilisée avec des spécialisations supplémentaires
supplémentaires par héritage. Cependant, l'entité d'origine
est verrouillée pour permettre au programme d'être à la fois ouvert et fermé.
public interface IServiceFilm
{
public double GetFilmDiscount(Film film);
}
public class ServiceFilmHorror : IServiceFilm
{
public double GetFilmDiscount(Film
iscount(Film film)
{
return film.Amount - 100;
}
}
public class ServiceFilmFiction : IServiceFilm
{
public double GetFilmDiscount(Film film)
{
return film.Amount - 50;
}
}
Le principe de substitution de Liskov stipule qu’une classe de base doit être substituable par ses
classes dérivées sans que cela nécessite des modifications.
En termes simples, lorsque nous avons une classe de base et des relations de classe dérivée, c'est-à-
c'est
dire des relations d'héritage,, et nous pouvons remplacer l'objet d'une classe de base par un objet de
la classe dérivée, sans affecter le comportement de l'instance
l'instance de la classe de base, alors on dit qu'il
est dans le principe de substitution de Liskov.
Le but de ce principe est de vous aider à éviter certains problèmes lors de la modélisation de vos
objets en vous faisant prendre conscience des problèmes potentiels
potentiels qui ne sont pas si évident
lorsque vous utilisez l’héritage au moment du développement.
Exemple qui viole le principe de substitution de Liskov
Dans l'exemple suivant, nous avons créé une classe Duck avec deux méthodes Walk() et Eat(). Nous
avons créé aussi la classe ElectricDuck qui hérite de la classe Duck ainsi que ses deux méthodes Eat()
et Walk().
Lorsque nous appelons la méthode Eat(), c'est-à-dire « duck.Eat() », nous obtenons une exception.
Cela signifie qu'une fois l'objet dérivé ElectricDuck est stocké dans objet parent Duck, le
comportement de ce dernier est également modifié. Ceci est contradictoire au principe LSP. Cette
dernière stipule que même si l'objet dérivé est remplacé par le parent, le comportement ne doit pas
être modifié.
L'idée de LSP est de s’assurer que les classes dérivées étendent les classes de base sans changer leurs
comportements.
Pour que notre exemple respecte le principe de Liskov, nous proposons la correction
suivante.
interface IDuck public virtual void Eat()
{ {
string Color { get; set; } "Duck eats");
Console.WriteLine("Duck eats"
void Walk(); }
} }
interface Iherbivorous public class ElectricDuck : IDuck
{ {
void Eat(); public string Color { get;
get set; }
}
public class Duck : IDuck, Iherbivorous public virtual void Walk()
{ {
public string Color { get; set;
set } "Electric Duck
Console.WriteLine("Electric
public virtual void Walk() walks");
{ }
Console.WriteLine("Duck walks"
"Duck walks"); }
}
Conformément au principe de responsabilité unique de SOLID, comme les classes, les interfaces
int
devraient également avoir une responsabilité unique. Cela signifie que nous ne devrions forcer
aucune classe à implémenter une ou plusieurs méthodes dont elles n'ont pas besoin.
Exemple qui viole le principe de ségrégation d’interface
Supposons que vous voulez construire une sorte d'imprimante ou de scanner. Il existe aussi
l’imprimante multifonction qui effectue à la fois l'impression, la numérisation et la photocopie.
Donc, vous décidez de créer une interface pour tout type d’imprimante, nommée IMachine, comme
suit.
public interface IMachine
{
public void Scan(Document d);
public void Print(Document d);
public void Fax(Document d);
}
Tant que vous travaillez avec une imprimante multifonction, tout va bien car vous implémentez
toutes les méthodes de cette interface IMachine, comme suit.
public class MultiFunctionPrinter : IMachine
{
public void Print(Document d)
{
// do any think
}
public void Fax(Document d)
{
// do any think
}
public void Scan(Document d)
{
// do any think
}
}
Et si vous décidez, par exemple, de créer juste une imprimante à l'ancienne. Elle ne pourrait ni
numériser ni télécopier des documents. Elle pourrait juste imprimer.
Le problème est que vous n'avez qu'une seule grande interface avec beaucoup de responsabilités.
responsabi La
question qui se pose est « que feriez-vous
feriez vous à propos de la numérisation et de la télécopie, jetez-vous
jetez
une exception ». Et c'est là qu'intervient le principe de ségrégation des interfaces.
L'idée est de s'assurer que les classes dérivées ne doivent
doivent pas dépendre des méthodes dont elles
n'ont pas besoin. Au lieu d'avoir une grande interface, vous devriez avoir beaucoup d'interfaces plus
petites qui sont plus atomiques, dirons-nous
dirons nous en ce sens qu'elles sont en quelques sortes autonomes
et qu'elles couvrent
vrent une préoccupation particulière.
Exemple qui respecte le principe de ségrégation d’interface
Donc, ce que vous devez faire dans notre cas est de créer trois interfaces plus petites, une pour
l'impression, une pour la numérisation, et une pour la télécopie, comme suit.
Le principe d'inversion de dépendances stipule que les modules de haut niveau du système ne
doivent pas dépendre directement des modules de bas niveau du système, mais plutôt d'une sorte
d'abstraction. Il stipule
le aussi que les abstractions ne doivent pas dépendre des détails. Les détails
devraient dépendre des abstractions
Ce principe vise principalement à réduire les dépendances entre les modules de code. Ceci est réalisé
par la définition des contrats pour les
les objets de bas niveau que les objets de haut niveau peuvent les
utiliser, sans qu’ils aient besoin de se soucier de l’implémentation spécifique fournie par les objets de
bas niveau.
Le DIP précise que dépendre des interfaces est moins risqué que de dépendre
dépendre d’implémentations
concrètes. Le fait d'avoir également des interfaces stables améliore la réutilisation du code. En
conséquence, ces interfaces bien conçues sont moins sujettes aux changements que les classes
concrètes qui les implémentent.
En offrant également une mise en œuvre qui dépend de l’interface, vous obtenez la possibilité de
choisir au moment de l’exécution, quelle implémentation est mieux adaptée à votre environnement
particulier.
Notez que la classe Copy est une classe de haut niveau. Elle dépend à la fois de la classe
KeyBoardReader et de la classe PrinterWriter, qui sont des classes de bas niveau. En d'autres termes,
la classe copie dépend de l’implémentation concrète des deux classes KeyBoardReader et
PrinterWriter, et non de l’abstraction. Étant donné que DIP veut que les classes de haut niveau et de
bas niveau dépendent d'abstractions, ce code viole actuellement le principe principe d'inversion de
dépendance.
Solution pour le principe d'inversion de dépendance :
Nous avons implémenté le principe d'inversion de dépendance dans notre exemple où les classes de
haut niveau (classe Copy) ne doivent pas dépendre directement des classesclasses de bas niveau (classes
KeyBoardReader et PrinterWritter), mais plutôt d'une sorte d'abstraction (IReader et IPrinter). De
plus, l'abstraction (IReader et IPrinter) ne dépend pas des détails (KeyBoardReader et PrinterWritter)
mais les détails dépendent dee l'abstraction.
public class Program public class PrinterWriter : IWriter
{ {
public static void Main() public void Write(string output)
{ {
// Send output to Printer Console.WriteLine(string.Format(
.Format("Writing to
IReader r = new KeyboardReader(); printer {0}", output));
IWriter pw = new PrinterWriter(); }
Copy cp = new Copy(r, pw); }
cp.DoWork(); public class FileWriter : IWriter
// Send output to FileStream now {
IWriter fw = new FileWriter(); public void Write(string output)
Copy cpf = new Copy(r, fw); {
cpf.DoWork(); Console.WriteLine(string.Format(
.Format("Writing to
Console.ReadLine(); file {0}", output));
} }
} }
public interface IReader public class Copy
{ string Read(); } {
public class KeyboardReader : IReader private IReader reader;
{ private IWriter writer;
public string Read() public Copy(IReader
(IReader reader, IWriter writer)
{ return Console.ReadLine(); } { this.reader = reader; this.writer
.writer = writer; }
} public void DoWork()
public interface IWriter { this.writer.Write(this.reader.Read());
.reader.Read()); }
{ void Write(string output); } }
L’objectif principal de l'inversion de dépendance est de supprimer les dépendances de votre code.
Elle vous permet d'intégrer une certaine tolérance au changement dans votre code, de confronter le
changementt et de l'accompagner, permettant à notre système de s'adapter aux nouvelles exigences
importantes et aux règles changeantes.
Références
[1] https://docs.microsoft.com/en-us/dotnet/architecture/modern
us/dotnet/architecture/modern-web-apps-azure/architectural
azure/architectural-principles
[2] https://www.dotnetcurry.com/software-gardening/1176/solid-open-closed-principle
https://www.dotnetcurry.com/software
[3] https://docs.microsoft.com/en
https://docs.microsoft.com/en-us/archive/msdn-magazine/2014/may/csharp-bestbest-practices-dangers-of-
violating-solid-principles-in-csharp
[4] http://blogs.developpeur.org/fathi/archive/2011/11/26/pr-sentation-des-principes-solid.aspx
http://blogs.developpeur.org/fathi/archive/2011/11/26/pr solid.aspx
Factory Pattern
Qu’est-ce
ce qu’un Factory Pattern ?
Le Factory Pattern est le modèle de conception le plus utilisé dans les langages de programmation
modernes comme Java et C#.
Le concept clé de ce pattern est d'abstraire la façon dont un objet est créé et instancié. Le but de
cette abstraction est d'éviter de coupler une implémentation avec des classes particulières ou la
manière dont chaque instance d'objet doit être créée et configurée. Le résultat est une
implémentation qui fonctionne comme un moyen abstrait pour la la création et l'initialisation d'objets,
qui à son tour suit le concept de séparation des préoccupations.
La séparation des préoccupations concerne les différents aspects de la fonctionnalité du logiciel. Par
exemple, la « logique métier" du logiciel est une
une préoccupation, et l'interface par laquelle une
personne utilise cette logique est une autre.
La séparation des préoccupations consiste à garder le code pour chacune de ces préoccupations
distinctes. La Modification de l'interface ne devrait pas exiger la
la modification du code de logique
métier, et vice versa.
Quels sont les avantages d’un Factory Pattern ?
Le Factory pattern est utilisé généralement pour séparer les responsabilités de création des objets.
Cette séparation permet :
● Aux gens de travailler sur des morceaux individuels du système dans l’isolement ;
● De faciliter la réutilisation ;
● D’assurer la maintenance d'un système ;
● D’ajouter des nouvelles fonctionnalités facilement ;
● De mieux comprendre le système ;
Repository
Qu’est-ce
ce qu’un Repository Pattern ?
Le repository pattern est un intermédiaire entre la couche métier et la couche d’accès aux données,
qui agit comme une collection d’objets en mémoire.
Le repository pattern est une couche d'abstraction qui fournit une approche bien organisée pour
maintenir une séparation entre l'accès aux données d'une application et les couches logiques métier.
Pour implémenter le repository pattern, vous pouvez créer une classe qui utilise la classe de contexte
et contient des méthodesdes telles que GetAll(), GetByID(), Insert(), Update() et Delete() qui vont
effectuer les opérations CRUD typiques sur une base de données.
Unit Of Work
Qu’est-ce qu’un Unit Of Work?
Une unité de travail est un modèle de conception décrit par Martin Fowler comme « maintenir une
liste d'objets affectés par une transaction commerciale et coordonne l'écriture des modifications et la
résolution des problèmes de concurrence. ».
En d'autres termes, l’Unit Of Work regroupe un ensemble d'opérations (d'insertion, de mise à jour,
de suppression, etc.) que nous souhaitons effectuer en une seule transaction, plutôt que de faire
plusieurs transactions sur une base de données. Il suit les changements qui nous intéressent jusqu'à
ce que nous soyons prêts à les enregistrer dans la base de données.
Quels sont les avantages d’un Unit Of Work ?
Unit Of Work est un design pattern qui répond à beaucoup de problèmes de développement et
apporte les avantages suivants :
● Il permet de garder en mémoire les modifications logiques de base de données dans un
ensemble cohérent.
● Il permet d’orchestrer les opérations de base sous forme de transactions pour pouvoir
annuler les modifications en cas de problèmes.
● Il permet d’isoler votre application des modifications de la couche de données et ainsi
faciliter les tests.
● Il coordonne le travail des différents Repositories en ne créant qu’un seul contexte partagé.
Références
[1]https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice
us/dotnet/architecture/microservices/microservice-ddd
ddd-cqrs-
patterns/infrastructure-persistence--layer-design
[2] https://docs.microsoft.com/en
https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started
started-with-ef-5-using-
mvc-4/implementing-the-repository
repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
application
[3]https://blog.dcube.fr/index.php/2019/09/05/generic
://blog.dcube.fr/index.php/2019/09/05/generic-repository-unit-of-work-et-entity
entity-framework/
[4]https://www.oodesign.com/factory
https://www.oodesign.com/factory-pattern.html
[5]https://www.codingame.com/playgrounds/36103/design
https://www.codingame.com/playgrounds/36103/design-pattern-factory-abstract-factory/design
factory/design-pattern-
factory