Est-ce que DRY est bon ou peut-il casser O de SOLID

Le principe de DRY (Do not Repeat Yourself) est depuis longtemps évident pour tout le monde et est apprécié par de nombreux programmeurs. Et beaucoup conviennent que copier / coller n'est pas cool du tout. Dans cet article, je veux donner un exemple de cas où dans la programmation industrielle l'utilisation de Copier / Coller est plus appropriée et aide à implémenter magnifiquement le principe Open-Closed de SOLID.

Permettez-moi de vous rappeler que le principe Open-closed encourage les programmeurs à concevoir des classes afin qu'elles soient ouvertes pour l'extension, mais en même temps fermées pour la modification. La modification d'une classe n'est autorisée que si une erreur est détectée dans la classe. Si vous souhaitez ajouter des fonctionnalités, le principe nécessite la création d'une nouvelle classe et l'utilisation de l'héritage ou de la mise en œuvre de la même interface.

Par exemple, il existe un système de gestion des colis. Supposons que la tâche soit venue d'ajouter la possibilité de créer, en plus des packages simples, des packages urgents.

Nous avons une classe Parcel qui décrit le fonctionnement d'un package standard:

public interface IParcel { string Barcode {get; set;} } public class Parcel: IParcel { public string Barcode {get; set;} } 

Il est très tentant d'ajouter simplement un champ à la fois à l'ancienne classe Parcel et à l'interface IParcel:

 public interface IParcel { string Barcode {get; set;} bool IsUrgent {get; set;} } public class Parcel: IParcel { public string Barcode {get; set;} public bool IsUrgent {get; set;} } 

Cependant, un tel code ne doit pas passer CodeReview! Un vérificateur de code strict et expérimenté devrait le renvoyer avec la remarque: "une telle implémentation viole le principe Open-closed."

Il est préférable de créer une nouvelle classe UrgentParcel, et vous n'aurez pas besoin de changer l'interface ou la classe Parcel. Les fichiers de classe et d'interface resteront intacts:

 public class UrgentParcel: IParcel { public string Barcode {get; set;} } 

Ce sera une observation du principe Open-closed, et un tel code ne recevra pas de commentaires avec CodeReview.

Revenons maintenant à DRY et à la manière dont il est difficile de mettre en œuvre le principe Open-closed.

Imaginez que dans la classe Parcel nous ayons le champ «status of the package» et une logique pour changer cet état:

 public class Parcel: IParcel { public string Barcode {get; set;} //   ( ,     ..) public ParcelStatuses Status {get; } // ,      "" public void ArrivedToRecipient(){ this.Status = ParcelStatuses.Arrived; } } 

Cette logique doit-elle être copiée dans la classe UrgentParcel? Le principe DRY ne dit en aucun cas. Il est préférable que la classe UrgentParcel hérite simplement de la classe Parcel, ce qui résout le problème et n'a pas à copier / coller le corps de la méthode ArrivedToRecipient dans la classe UrgentParcel.

Cependant, si vous ne copiez pas le code, mais que vous en héritez, les modifications apportées à la méthode ArrivedToRecipient dans la classe Parcel entraîneront immédiatement un changement dans le comportement de la classe UrgentParcel, ce qui violera le principe Open-closed. C'est vraiment une violation, car la tâche avec les colis urgents (implémentation de la classe UrgentParcel) a déjà été livrée, testée et, par conséquent, fonctionne «au combat». Ainsi, cette logique, implémentée dans la méthode UrgentParcel.ArrivedToRecipient et appliquée aux colis urgents, convient à tout le monde et ne devrait PAS changer lorsque le travail d'autres types de colis change. Ainsi, le principe Open-closed est précisément conçu pour protéger le système contre de telles actions par des programmeurs juniors inexpérimentés qui, tout en résolvant la tâche, comme d'habitude "face à face", ne réalisent pas encore toutes les dépendances, et leurs changements en un seul endroit affectent de nombreux autres domaines fonctionnels .

Habituellement, l'un des principaux arguments en faveur de DRY est le fait que si une erreur est trouvée dans la méthode ArrivedToRecipient, elle doit être corrigée partout où elle a été copiée. Donc, cela n'a tout simplement pas besoin d'être fait. Si une erreur est trouvée dans la méthode ArrivedToRecipient lors de l'utilisation de packages standard, il est nécessaire de corriger le travail des packages ordinaires. Mais personne ne s'est plaint du travail des colis urgents et, probablement, tout le monde est satisfait du fonctionnement des colis urgents.

Pour les perfectionnistes, à qui je me considère également, je proposerais de laisser un commentaire qui nous permettrait de ne pas oublier tous les endroits où la méthode a été copiée, et de poser la question: cette méthode fonctionne-t-elle correctement pour les colis urgents?

Voici un exemple d'un tel commentaire:

 public class Parcel: IParcel{ ... /// <summary> ///    "" /// </summary> /// <remarks>NOTE:     : <see cref="UrgentParcel"/></remarks> public void ArrivedToRecipient(){ ... } } public class UrgentParcel: IParcel{ ... /// <summary> ///    "" /// </summary> /// <remarks>NOTE:      : <see cref="Parcel"/></remarks> public void ArrivedToRecipient(){ ... } } 

L'avis de la communauté sur cette question est très intéressant. Merci d'avance pour vos commentaires.

Source: https://habr.com/ru/post/fr423515/


All Articles