”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 掌握 SOLID 原则:干净高效的代码

掌握 SOLID 原则:干净高效的代码

发布于2024-11-08
浏览:361

Dans le monde du développement, il y a un grand fossé entre écrire du code et écrire du bon code. Tu sais, ce code que tu peux modifier dans 6 mois sans avoir à tout réécrire, ce code qui ne t’oblige pas à passer des heures à le comprendre avant de le débugger.

Et si je te disais qu’il existe une méthode pour structurer ton code de manière à le rendre facilement maintenable, modulaire, et sans prise de tête ? Accueillons sans plus attendre les principes SOLID.

Ces cinq principes, introduits par l'incontournable Robert C. Martin, aka Uncle Bob, montrent comment organiser tes classes et tes méthodes pour qu’elles puissent évoluer sans s’effondrer.

Tu es prêt à devenir un développeur plus ? SOLID ? que jamais ? On y va. ?


1. Le * S * ➡️ Single Responsibility Principle

"Une classe ne doit avoir qu'une seule responsabilité."

L'idée est simple : chaque classe doit avoir une seule raison de changer. Cela signifie qu'une classe ne doit s'occuper que d'une seule tâche ou d'une seule fonctionnalité. Si tu mélanges plusieurs responsabilités dans une même classe, tu finiras par complexifier ton code et rendre les modifications plus risquées.

Exemple avec Symfony

Prenons l'exemple d'une classe qui gère à la fois la validation d'un formulaire d'utilisateur et l'envoi d'emails de confirmation. Voici un exemple de ce qu'il ne faut pas faire :

Maîtriser les principes SOLID : pour un code propre et performant

Ici, la classe UserService viole le principe de responsabilité unique. Elle gère à la fois la création de l'utilisateur, la validation des données, et l'envoi d'un email. Chaque fois que l'une de ces responsabilités évolue, la classe doit être modifiée.

Solution : On divise les responsabilités !

Maîtriser les principes SOLID : pour un code propre et performant

Maintenant, chaque classe a une responsabilité distincte : UserValidator pour la validation, EmailService pour l'envoi d'emails et UserService pour la logique de gestion des utilisateurs. Ton code devient beaucoup plus facile à maintenir !


2. Le * O * ➡️ Open/Closed Principle

"Les entités doivent être ouvertes à l'extension, mais fermées à la modification."

Cela signifie que tu dois pouvoir ajouter de nouvelles fonctionnalités à une classe sans avoir à modifier son code existant.
Pourquoi ? Parce que chaque fois que tu modifies du code existant, tu risques d'introduire des bugs. Mieux vaut ajouter des fonctionnalités que de retoucher le code déjà écrit et testé.

Exemple avec Symfony

Imaginons que tu développes un service de paiement dans une boutique Symfony. Ce service gère différentes méthodes de paiement : carte de crédit, PayPal, etc. Si tu veux ajouter un nouveau mode de paiement (Bitcoin, par exemple), le principe Open/Closed te dit qu'il ne faut pas toucher au code déjà existant pour éviter les régressions.

Voici une mauvaise approche, où l'ajout de nouvelles fonctionnalités nécessite des modifications dans le code existant :

Maîtriser les principes SOLID : pour un code propre et performant

Le problème ici, c'est que chaque fois que tu veux ajouter un nouveau moyen de paiement, tu dois modifier la méthode processPayment, ce qui viole le principe Ouvert/Fermé.

Solution : Utiliser l'héritage et les interfaces

Pour résoudre ce problème et respecter le principe Ouvert/Fermé, tu vas utiliser une interface et des classes spécifiques pour chaque type de paiement. Comme ça, tu pourras ajouter de nouvelles méthodes de paiement sans toucher au code existant.

Maîtriser les principes SOLID : pour un code propre et performant
Maîtriser les principes SOLID : pour un code propre et performant
Maîtriser les principes SOLID : pour un code propre et performant

Quelques explications

  • Chaque méthode de paiement implémente l'interface PaymentMethodInterface, ce qui garantit qu'elles ont toutes la méthode pay().
  • Dans chaque classe, on utilise l'attribut #[AsTaggedItem('payment.method')] pour dire à Symfony que ces classes représentent des méthodes de paiement. Elles sont toutes taguées sous le même nom, payment.method, pour être facilement récupérables.

Le service principal PaymentService

Maintenant, notre PaymentService doit savoir quelle méthode de paiement utiliser, celle qui a été sélectionnée par l'utilisateur par exemple. Voici comment créer le PaymentService pour qu'il prenne en compte la méthode de paiement envoyée par le contrôleur.

Maîtriser les principes SOLID : pour un code propre et performant

Dans ton contrôleur, tu vas récupérer la méthode de paiement depuis la requête (via un query parameter par exemple) et la passer au PaymentService. Ton controller pourrait ressembler à ça :

Maîtriser les principes SOLID : pour un code propre et performant

Il est important que chaque méthode soit taguée correctement pour être injectée dans le PaymentService grâce au TaggedIterator.

En résumé, l'itérable $paymentMethods dans le PaymentService permet de récupérer dynamiquement toutes les méthodes de paiement disponibles, et le contrôleur passe cette information en fonction de la requête. Le service choisit ensuite la bonne méthode en fonction de la requête et procède au paiement.

Cette approche respecte le principe **open/closed tout en exploitant les fonctionnalités modernes de Symfony pour rendre le code plus flexible et extensible.


3. Le * L * ➡️ Liskov Substitution Principle

"Les objets d'une classe dérivée doivent pouvoir remplacer les objets de leur classe mère sans altérer le bon fonctionnement du programme."

Ce principe peut paraître un peu complexe à première vue, mais il est essentiel pour assurer la cohérence de ton code. En résumé, une classe fille doit pouvoir être utilisée partout où une classe mère est attendue, sans briser le comportement du programme.

Exemple avec Symfony

Prenons une classe Article et une classe FeaturedArticle, qui hérite de Article. Si FeaturedArticle ne respecte pas le contrat de la classe mère Article, tu risques d’avoir des problèmes lors de son utilisation dans certaines parties de ton code.

Voici un cas qui viole le principe de Liskov :

Maîtriser les principes SOLID : pour un code propre et performant

À première vue, ça peut sembler correct, mais si tu passes un FeaturedArticle dans une logique qui attend un Article, tu risques d’avoir des résultats inattendus.

Par exemple, imagine un système où les titres sont mis à jour automatiquement :

Maîtriser les principes SOLID : pour un code propre et performant

Le titre sera "Featured: Nouveautés", même si ce n'était pas prévu.

Solution

Pour respecter le principe de Liskov, tu dois faire en sorte que les classes dérivées n'altèrent pas le comportement attendu de la classe mère. Par exemple avec Symfony, si tu as un formulaire de base pour un Article, et que tu veux étendre son comportement pour un FeaturedArticle, tu peux étendre le FormType tout en conservant les propriétés de base.

1. Crée un FormType pour un article de base :

Maîtriser les principes SOLID : pour un code propre et performant

2. Crée un FormType pour un FeaturedArticle qui hérite de ArticleType :

Maîtriser les principes SOLID : pour un code propre et performant

Dans le contrôleur, utilise ce FormType pour un article à la une :

Maîtriser les principes SOLID : pour un code propre et performant

Avec cette approche, le principe de Liskov est respecté. Tu peux utiliser FeaturedArticleType partout où ArticleType est utilisé, tout en ajoutant des champs spécifiques sans casser le comportement de base.


4. Le * I * ➡️ Interface Segregation Principle

"Une classe ne doit pas être obligée d'implémenter des méthodes qu'elle n'utilisera jamais."

Si tu veux garder ton code flexible, ce principe est primordial. Il dit que si une classe implémente une interface, elle ne doit implémenter que ce dont elle a besoin. Si l'interface est trop générale et oblige une classe à implémenter des méthodes inutiles, elle deviendra plus complexe et difficile à maintenir.

Exemple avec Symfony

Imaginons que tu aies une interface pour un export de données, avec plusieurs types d’export (CSV, JSON, XML). Si tu crées une interface unique pour tout, tu ne respectes pas le principe de ségrégation des interfaces :

Maîtriser les principes SOLID : pour un code propre et performant

Si tu implémentes cette interface dans une classe qui n'a besoin que de JSON, tu te retrouves à devoir définir des méthodes inutiles, comme exportToCSV() et exportToXML().

Solution : Créer des interfaces spécifiques

La solution est de diviser cette interface en plusieurs petites interfaces qui sont spécifiques à chaque besoin :

Maîtriser les principes SOLID : pour un code propre et performant

Maintenant, chaque classe implémente seulement les interfaces dont elle a besoin :

Maîtriser les principes SOLID : pour un code propre et performant

Ton code est plus léger et maintenable, sans méthodes superflues !


5. Le * D * :arrow_rigt: Dependency Inversion Principle

"Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Tous deux doivent dépendre d'abstractions."

? Dernier principe ! ?
Si tu es arrivé jusque là, je te félicite car ce n'était pas simple !

Alors, qu'est-ce qu'il nous dit ce dernier principe ?
Il nous dit tout simplement de ne pas dépendre de classes concrètes, mais plutôt d’abstractions (des interfaces ou des classes abstraites).

Exemple avec Symfony

Imaginons un service qui envoie des notifications. Une mauvaise approche serait de faire dépendre directement ce service des implémentations concrètes :

Maîtriser les principes SOLID : pour un code propre et performant

Ici, on voit que ce service dépend directement des implémentations concrètes d'email et de SMS (les classes EmailService et SmsService). Si tu veux changer le système d'envoi d'email, il faudra que tu modifies cette classe.

Solution

Une meilleure approche est de faire en sorte que NotificationService dépende d'une abstraction (une interface), plutôt que d'implémentations concrètes :

Interface pour les services de notification :
Maîtriser les principes SOLID : pour un code propre et performant

Implémentation pour les emails ? :
Maîtriser les principes SOLID : pour un code propre et performant

Implémentation pour les SMS ? :
Maîtriser les principes SOLID : pour un code propre et performant

Le service NotificationService dépend maintenant de NotificationInterface, ce qui te permet de changer facilement l'implémentation sans toucher au service principal.

Maintenant on va injecter dynamiquement ces services dans un NotificationManager :

Maîtriser les principes SOLID : pour un code propre et performant

Utilisation dans le contrôleur

Dans le contrôleur, on peut choisir dynamiquement le service de notification à utiliser :

Maîtriser les principes SOLID : pour un code propre et performant

  • On injecte toutes les implémentations de NotificationServiceInterface automatiquement grâce au tag #[AsTaggedItem('notification.service')] sur chaque service.
  • Le contrôleur peut choisir dynamiquement quel service utiliser via une query string dans la requête.

Cette solution suit parfaitement le dernier principe SOLID car :

  • Le NotificationManager ne dépend que de l'interface NotificationServiceInterface, et non des implémentations spécifiques.
  • Le code est ouvert à l'ajout de nouveaux services (par exemple, une notification par push) sans modifier le NotificationManager.

Conclusion

Et voilà ! Tu as maintenant une solide ? compréhension des principes SOLID et de leur application dans un projet Symfony. Le but de ces principes est de rendre ton code plus flexible, facile à maintenir, et surtout testable.

Ces concepts peuvent parfois sembler abstraits, mais lorsqu'ils sont appliqués, ils t'offrent un énorme gain en termes de qualité et de durabilité du code !

版本声明 本文转载于:https://dev.to/technivek/maitriser-les-principes-solid-pour-un-code-propre-et-performant-4a4m?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 使用CTE高效拆分SQLite中的逗号分隔字符串
    使用CTE高效拆分SQLite中的逗号分隔字符串
    Splitting Comma-Separated Values with SQLite's Common Table ExpressionsQuestion:How can I effortlessly split a comma-separated string in the Categ...
    编程 发布于2025-04-18
  • 如何让最小宽度媒体查询正确级联?
    如何让最小宽度媒体查询正确级联?
    使用最小宽度媒体查询在设计网站时,使用响应式网络设计原理(包括移动 - 移动范围的方法)是常见的实践。这需要使用最小宽度媒体查询来针对特定屏幕尺寸的设备。但是,当覆盖更高屏幕分辨率的CSS值时,这可能会令人困惑,因为较低的最小宽度似乎优先。这是因为对媒体查询的评估是从最小限度地限制到最限制的。在提...
    编程 发布于2025-04-18
  • 图片在Chrome中为何仍有边框?`border: none;`无效解决方案
    图片在Chrome中为何仍有边框?`border: none;`无效解决方案
    在chrome 中删除一个频繁的问题时,在与Chrome and IE9中的图像一起工作时,遇到了一个频繁的问题。和“边境:无;”在CSS中。要解决此问题,请考虑以下方法: Chrome具有忽略“ border:none; none;”的已知错误,风格。要解决此问题,请使用以下CSS ID块创建带...
    编程 发布于2025-04-18
  • 如何使用PHP将斑点(图像)正确插入MySQL?
    如何使用PHP将斑点(图像)正确插入MySQL?
    essue VALUES('$this->image_id','file_get_contents($tmp_image)')";This code builds a string in PHP, but the function call ...
    编程 发布于2025-04-18
  • JavaScript中如何动态访问全局变量?
    JavaScript中如何动态访问全局变量?
    在JavaScript 一种方法是使用窗口对象存储和检索变量。通过引用全局范围,可以使用其名称动态访问变量。 //一个脚本 var somevarname_10 = 20; //另一个脚本 window.all_vars = {}; window.all_vars ['somevarnam...
    编程 发布于2025-04-18
  • 在C#中如何高效重复字符串字符用于缩进?
    在C#中如何高效重复字符串字符用于缩进?
    在基于项目的深度下固定字符串时,重复一个字符串以进行凹痕,很方便有效地有一种有效的方法来返回字符串重复指定的次数的字符串。使用指定的次数。 constructor 这将返回字符串“ -----”。 字符串凹痕= new String(' - ',depth); console.Wr...
    编程 发布于2025-04-18
  • 如何正确使用与PDO参数的查询一样?
    如何正确使用与PDO参数的查询一样?
    在pdo 中使用类似QUERIES在PDO中的Queries时,您可能会遇到类似疑问中描述的问题:此查询也可能不会返回结果,即使$ var1和$ var2包含有效的搜索词。错误在于不正确包含%符号。通过将变量包含在$ params数组中的%符号中,您确保将%字符正确替换到查询中。没有此修改,PDO...
    编程 发布于2025-04-18
  • 在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    For Each Loop vs. Iterator: Efficiency in Collection TraversalIntroductionWhen traversing a collection in Java, the choice arises between using a for-...
    编程 发布于2025-04-18
  • 如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求模拟浏览器行为,以及伪造的用户代理提供了一个用户 - 代理标头一个有效方法是提供有效的用户式header,以提供有效的用户 - 设置,该标题可以通过browser和Acterner Systems the equestersystermery和操作系统。通过模仿像Chro...
    编程 发布于2025-04-18
  • 版本5.6.5之前,使用current_timestamp与时间戳列的current_timestamp与时间戳列有什么限制?
    版本5.6.5之前,使用current_timestamp与时间戳列的current_timestamp与时间戳列有什么限制?
    在时间戳列上使用current_timestamp或MySQL版本中的current_timestamp或在5.6.5 此限制源于遗留实现的关注,这些限制需要对当前的_timestamp功能进行特定的实现。 创建表`foo`( `Productid` int(10)unsigned not n...
    编程 发布于2025-04-18
  • Flatten与Ravel:NumPy函数选择指南
    Flatten与Ravel:NumPy函数选择指南
    了解Numpy的Flatten和Ravel functions Numpy库提供两种方法,Flatten and ravel,以将多维数组转换为一维数组。但是,出现了一个问题:为什么要执行相同任务的两个不同的函数?相同的输出,不同的行为 打印(y.ravel()) [1 2 3 4 5 6 7 ...
    编程 发布于2025-04-18
  • 在细胞编辑后,如何维护自定义的JTable细胞渲染?
    在细胞编辑后,如何维护自定义的JTable细胞渲染?
    在JTable中维护jtable单元格渲染后,在JTable中,在JTable中实现自定义单元格渲染和编辑功能可以增强用户体验。但是,至关重要的是要确保即使在编辑操作后也保留所需的格式。在设置用于格式化“价格”列的“价格”列,用户遇到的数字格式丢失的“价格”列的“价格”之后,问题在设置自定义单元格...
    编程 发布于2025-04-18
  • 如何使用PHP从XML文件中有效地检索属性值?
    如何使用PHP从XML文件中有效地检索属性值?
    从php $xml = simplexml_load_file($file); foreach ($xml->Var[0]->attributes() as $attributeName => $attributeValue) { echo $attributeName,...
    编程 发布于2025-04-18
  • 如何在Chrome中居中选择框文本?
    如何在Chrome中居中选择框文本?
    选择框的文本对齐:局部chrome-inly-ly-ly-lyly solument 您可能希望将文本中心集中在选择框中,以获取优化的原因或提高可访问性。但是,在CSS中的选择元素中手动添加一个文本 - 对属性可能无法正常工作。初始尝试 state)</option> < op...
    编程 发布于2025-04-18

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3