Das Prinzip von DRY (Wiederholen Sie sich nicht) ist seit langem für alle offensichtlich und wird von vielen Programmierern geliebt. Und viele sind sich einig, dass Kopieren / Einfügen überhaupt nicht cool ist. In diesem Artikel möchte ich ein Beispiel geben, wann in der industriellen Programmierung die Verwendung von Kopieren / Einfügen angemessener ist und dazu beiträgt, das Open-Closed-Prinzip von SOLID wunderbar zu implementieren.
Ich möchte Sie daran erinnern, dass das Open-Closed-Prinzip Programmierer dazu ermutigt, Klassen so zu gestalten, dass sie für Erweiterungen geöffnet, aber gleichzeitig für Änderungen geschlossen sind. Das Ändern einer Klasse ist nur zulässig, wenn in der Klasse ein Fehler festgestellt wird. Wenn Sie Funktionen hinzufügen möchten, müssen Sie nach dem Prinzip eine neue Klasse erstellen und entweder die Vererbung oder die Implementierung derselben Schnittstelle verwenden.
Zum Beispiel gibt es ein Paketverwaltungssystem. Angenommen, die Aufgabe ist gekommen, die Fähigkeit hinzuzufügen, neben einfachen Paketen auch dringende zu erstellen.
Wir haben eine Paketklasse, die beschreibt, wie ein reguläres Paket funktioniert:
public interface IParcel { string Barcode {get; set;} } public class Parcel: IParcel { public string Barcode {get; set;} }
Es ist sehr verlockend, einfach ein Feld sowohl zur alten Paketklasse als auch zur IParcel-Schnittstelle hinzuzufügen:
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;} }
Ein solcher Code sollte jedoch CodeReview nicht bestehen! Ein strenger und erfahrener Codeprüfer sollte ihn mit der Bemerkung zurückgeben: "Eine solche Implementierung verstößt gegen das Open-Closed-Prinzip."
Es ist viel besser, eine neue UrgentParcel-Klasse zu erstellen, und Sie müssen weder die Schnittstelle noch die Parcel-Klasse ändern. Klassen- und Schnittstellendateien bleiben unberührt:
public class UrgentParcel: IParcel { public string Barcode {get; set;} }
Dies ist eine Einhaltung des Open-Closed-Prinzips, und dieser Code erhält keine Kommentare mit CodeReview.
Kommen wir nun zu DRY und der Art und Weise zurück, wie es schwierig ist, das Open-Closed-Prinzip umzusetzen.
Stellen Sie sich vor, wir haben in der Paketklasse das Feld "Status des Pakets" und eine Logik zum Ändern dieses Status:
public class Parcel: IParcel { public string Barcode {get; set;}
Muss diese Logik in die UrgentParcel-Klasse kopiert werden? Das DRY-Prinzip besagt das keineswegs. Es ist viel besser, dass die UrgentParcel-Klasse einfach von der Parcel-Klasse erbt, wodurch das Problem gelöst wird und der Hauptteil der ArrivedToRecipient-Methode nicht in die UrgentParcel-Klasse kopiert / eingefügt werden muss.
Wenn Sie den Code jedoch nicht kopieren, sondern erben, führen Änderungen an der ArrivedToRecipient-Methode in der Parcel-Klasse sofort zu einer Änderung des Verhaltens der UrgentParcel-Klasse, wodurch das Open-Closed-Prinzip verletzt wird. Dies ist wirklich ein Verstoß, da die Aufgabe mit dringenden Paketen (Implementierung der UrgentParcel-Klasse) bereits geliefert, getestet wurde und daher „im Kampf“ funktioniert. Diese Logik, die in der UrgentParcel.ArrivedToRecipient-Methode implementiert und auf dringende Pakete angewendet wird, eignet sich für alle und sollte sich NICHT ändern, wenn sich die Arbeit anderer Pakettypen ändert. Das Open-Closed-Prinzip ist daher genau darauf ausgelegt, das System vor solchen Aktionen durch unerfahrene Junior-Programmierer zu schützen, die bei der Lösung der Aufgabe wie üblich „frontal“ noch nicht alle Abhängigkeiten erkennen und deren Änderungen an einem Ort viele andere Funktionsbereiche betreffen .
Normalerweise ist eines der Hauptargumente für DRY die Tatsache, dass ein Fehler, der in der ArrivedToRecipient-Methode gefunden wird, überall dort behoben werden muss, wo er kopiert wurde. Das muss also einfach nicht gemacht werden. Wenn bei der Arbeit mit regulären Paketen ein Fehler in der ArrivedToRecipient-Methode gefunden wird, muss die Arbeit normaler Pakete korrigiert werden. Aber niemand hat sich über die Arbeit dringender Pakete beschwert, und wahrscheinlich sind alle zufrieden damit, wie dringende Pakete funktionieren.
Für Perfektionisten, für die ich mich auch halte, würde ich vorschlagen, einen Kommentar zu hinterlassen, der es uns ermöglicht, nicht alle Stellen zu vergessen, an denen die Methode kopiert wurde, und die Frage zu stellen: Funktioniert diese Methode für dringende Pakete korrekt?
Hier ist ein Beispiel für einen solchen Kommentar:
public class Parcel: IParcel{ ...
Sehr interessante Community-Meinung zu diesem Thema. Vielen Dank im Voraus für Ihre Kommentare.