===== S ====
Dans le principe SOLID, le "S" représente le principe de Responsabilité Unique (Single Responsibility Principle). En C#, cela signifie que chaque classe doit avoir une seule raison de changer. Par exemple, une classe qui gère la logique métier et l'affichage devrait être séparée en deux classes distinctes, une pour la logique métier et une autre pour l'affichage. Cela rend le code plus lisible, maintenable et évite les effets secondaires inattendus.
// Classe normale
public class Employee
{
public string Name { get; set; }
public decimal CalculatePay()
{
// Logique de calcul de la paie
return 0;
}
public void Save()
{
// Logique de sauvegarde dans la base de données
}
}
// Après l'application du principe de
//Responsabilité Unique
public class Employee
{
public string Name { get; set; }
public decimal CalculatePay()
{
// Logique de calcul de la paie
return 0;
}
}
public class EmployeeRepository
{
public void Save(Employee employee)
{
// Logique de sauvegarde dans la base de données
}
}
Dans cet exemple, la classe Employee a été séparée en deux classes distinctes. Une classe s'occupe uniquement de la logique métier relative aux employés, tandis que l'autre classe est responsable de la sauvegarde des employés dans la base de données. Cela respecte le principe de Responsabilité Unique, améliorant ainsi la maintenabilité et la lisibilité du code.
===== O =====
Le "O" dans SOLID représente le principe d'Ouverture/Fermeture (Open/Closed Principle). Ce principe stipule qu'une classe doit être ouverte à l'extension mais fermée à la modification. En d'autres termes, on devrait pouvoir étendre le comportement d'une classe sans la modifier.
En C#, cela peut être mis en œuvre à l'aide de l'héritage et des interfaces.
// Classe de base
public abstract class Shape
{
public abstract double Area();
}
// Sous-classes étendant la classe de base
public class Circle : Shape
{
public double Radius { get; set; }
public override double Area()
{
return Math.PI * Radius * Radius;
}
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double Area()
{
return Width * Height;
}
}
// Exemple d'utilisation
public class AreaCalculator
{
public double TotalArea(Shape[] shapes)
{
double total = 0;
foreach (var shape in shapes)
{
total += shape.Area();
}
return total;
}
}
Dans cet exemple, la classe Shape est ouverte à l'extension car de nouvelles formes peuvent être ajoutées en créant de nouvelles sous-classes de Shape (par exemple, Triangle, Square) sans modifier la classe AreaCalculator. Ainsi, la classe Shape respecte le principe d'Ouverture/Fermeture.
==== L ====
Le "L" dans SOLID représente le principe du Remplacement de Liskov (Liskov Substitution Principle). Ce principe stipule que les objets d'une classe de base doivent être remplaçables par des objets de ses classes dérivées sans affecter le comportement correct du programme.
public class Rectangle
{
public virtual int Width { get; set; }
public virtual int Height { get; set; }
public int GetArea()
{
return Width * Height;
}
}
public class Square : Rectangle
{
public override int Width
{
set { base.Width = base.Height = value; }
}
public override int Height
{
set { base.Width = base.Height = value; }
}
}
```
Dans cet exemple, la classe Square hérite de la classe Rectangle, mais les propriétés Width et Height sont modifiées pour respecter les caractéristiques d'un carré. En utilisant le principe de substitution de Liskov, un objet de type Rectangle peut être remplacé par un objet de type Square sans altérer le comportement attendu de la classe de base.
==== I ====
Le "I" dans SOLID fait référence au principe d'Interface Segregation Principle (Principe de Ségrégation des Interfaces). Ce principe stipule qu'il est préférable d'avoir de nombreuses interfaces spécifiques plutôt qu'une seule interface générale.
// Interface spécifique pour les opérations de lecture
public interface IReadable
{
void Read();
}
// Interface spécifique pour les opérations d'écriture
public interface IWritable
{
void Write();
}
// Classe utilisant l'interface IReadable
public class FileReader : IReadable
{
public void Read()
{
// Logique de lecture depuis un fichier
}
}
// Classe utilisant l'interface IWritable
public class ConsoleWriter : IWritable
{
public void Write()
{
// Logique d'écriture sur la console
}
}
Dans cet exemple, au lieu d'avoir une interface unique comprenant à la fois la lecture et l'écriture, nous avons des interfaces distinctes pour chaque type d'opération. Cela permet aux classes de mettre en œuvre uniquement les interfaces dont elles ont besoin, respectant ainsi le principe de Ségrégation des Interfaces.
==== D ====
Le “D” dans SOLID fait référence au principe d'Inversion des Dépendances (Dependency Inversion Principle). Ce principe stipule que les modules de haut niveau ne devraient pas dépendre des modules de bas niveau. Au lieu de cela, les deux devraient dépendre de l'abstraction. En outre, les détails doivent dépendre des abstractions, et non l'inverse.
// Interface
public interface ILogger
{
void LogMessage(string message);
}
// Classe de haut niveau dépendant de l'abstraction ILogger
public class ErrorLogger
{
private readonly ILogger _logger;
public ErrorLogger(ILogger logger)
{
_logger = logger;
}
public void LogError(string errorMessage)
{
_logger.LogMessage("Error: " + errorMessage);
}
}
// Classes de bas niveau implémentant l'interface ILogger
public class ConsoleLogger : ILogger
{
public void LogMessage(string message)
{
Console.WriteLine("Logging to console: " + message);
}
}
public class FileLogger : ILogger
{
public void LogMessage(string message)
{
// Log to file implementation
}
}
```
Dans cet exemple, la classe ErrorLogger dépend de l'abstraction ILogger plutôt que de dépendre directement des implémentations concrètes. Cela permet une plus grande flexibilité, car différentes implémentations de ILogger peuvent être utilisées sans modifier la classe ErrorLogger, respectant ainsi le principe d'Inversion des Dépendances.
============================
===========================================
SOLID Principles in Software Design
SOLID is an acronym representing five fundamental principles for object-oriented programming and software design. These principles, introduced by Robert C. Martin (also known as Uncle Bob), aim to make software more maintainable, scalable, and easier to understand. Here's an overview of each principle:
1. Single Responsibility Principle (SRP)
"A class should have only one reason to change."
Explanation: Each class should be responsible for a single part of the software's functionality. This makes classes simpler and ensures they focus on doing one thing well.
Benefits:
Improved maintainability.
Easier to test and debug.
Example:
Instead of a single Employee class handling both employee data and payroll calculation, split it into:
Employee (data handling).
Payroll (calculation logic).
2. Open/Closed Principle (OCP)
"Software entities should be open for extension but closed for modification."
Explanation: Classes should be designed so that they can be extended to accommodate new behavior without modifying existing code. This reduces the risk of introducing bugs when adding new functionality.
Benefits:
Promotes code stability.
Encourages flexibility.
Example:
Use inheritance or interfaces to extend functionality:
A Shape base class with Area() method.
Add new shapes like Circle or Rectangle by extending Shape instead of modifying it.
3. Liskov Substitution Principle (LSP)
"Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program."
Explanation: Subclasses must respect the contract of their parent class. A derived class should only extend the behavior of the base class, not break it.
Benefits:
Avoids unexpected behavior.
Ensures polymorphism works correctly.
Example:
If a class Bird has a method Fly(), a subclass Penguin should not override it with behavior that breaks expectations (since penguins can’t fly). Instead, refactor the hierarchy to differentiate between flying and non-flying birds.
4. Interface Segregation Principle (ISP)
"A class should not be forced to implement interfaces it does not use."
Explanation: Large interfaces should be divided into smaller, more specific ones. This ensures that classes only implement methods that are relevant to them.
Benefits:
Reduces code bloat.
Simplifies implementation and testing.
Example:
Instead of one large Animal interface with methods like Fly(), Swim(), and Run(), create smaller interfaces:
IFlyable, ISwimmable, IRunnable.
5. Dependency Inversion Principle (DIP)
"Depend on abstractions, not on concrete implementations."
Explanation: High-level modules should not depend on low-level modules; both should depend on abstractions (e.g., interfaces or abstract classes). This decouples modules and makes the system easier to change.
Benefits:
Increases modularity.
Makes code easier to test.
Example:
Instead of a class Car depending directly on a DieselEngine class, it should depend on an abstract IEngine interface. This allows swapping implementations (e.g., ElectricEngine) without modifying Car.
Benefits of SOLID Principles
Improved maintainability.
Easier debugging and testing.
Reduced code duplication.
Scalability for future requirements.
Enhanced code readability and design consistency.
Aucun commentaire:
Enregistrer un commentaire