The principle of DRY (Do not Repeat Yourself) is obvious to everyone for a long time and is loved by many programmers. And many agree that Copy / Paste is not cool at all. In this article I want to give an example of when the use of Copy / Paste in industrial programming is more appropriate and helps to beautifully implement the Open-Closed principle from SOLID.
Let me remind you that the Open-closed principle encourages programmers to design classes in such a way that they are open for expansion, but closed for modification. Modifying a class is allowed only if an error is detected in the class. If you want to add functionality, the principle calls for creating a new class and using either inheritance or the implementation of the same interface.
For example, there is a parcel control system. Let's say the task came to add the ability to create, in addition to simple packages, also urgent ones.
We have the Parcel class, which describes the work of a regular package:
')
public interface IParcel { string Barcode {get; set;} } public class Parcel: IParcel { public string Barcode {get; set;} }
It is very tempting to simply add a field to the old Parcel class and to the IParcel interface:
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;} }
However, such code should not pass CodeReview! The strict and experienced "checker" of the code should return it with the remark: "such an implementation violates the Open-closed principle."
It is much better to create a new UrgentParcel class, and neither the interface nor the Parcel class will need to be changed. The class and interface files will remain intact:
public class UrgentParcel: IParcel { public string Barcode {get; set;} }
This will be the observance of the Open-closed principle, and such code will not receive comments with CodeReview.
Now let's go back to DRY and how it interferes with the implementation of the Open-closed principle.
Imagine that in the Parcel class we have the field “parcel status” and some logic to change this status:
public class Parcel: IParcel { public string Barcode {get; set;}
Does this logic need to be copied to the UrgentParcel class? The principle of DRY says that by no means. It is much better that the UrgentParcel class simply inherits from the Parcel class, which solves the problem and does not have to Copy / Paste the body of the ArrivedToRecipient method into the UrgentParcel class.
However, if you do not copy the code, but inherit it, then changes to the ArrivedToRecipient method in the Parcel class will immediately change the behavior of the UrgentParcel class, which will violate the Open-closed principle. This is really a violation, because the task with urgent parcels (the implementation of the UrgentParcel class) has already been handed over, tested and, as a result, works “in combat”. So, this logic, implemented in the UrgentParcel.ArrivedToRecipient method and applied to urgent packages, suits everyone and it should NOT change when other types of packages change. So, the Open-closed principle is precisely intended to protect the system from such actions of inexperienced junior programmers, who, while solving the problem, as usual “head on,” do not yet realize all the dependencies, and their changes in one place affect many other functional areas. .
Usually, one of the main arguments in favor of DRY is the fact that if an error is found in the ArrivedToRecipient method, then it must be corrected wherever it is copied. So this, just, do not need. If an error is found in the ArrivedToRecipient method when working with regular parcels, then it is necessary to correct the operation of ordinary parcels. And nobody complained about the work of urgent parcels and, probably, everyone is satisfied with how urgent parcels work.
For perfectionists, to whom I also rank myself, I would suggest leaving a comment that would allow not to forget about all the places where the method was copied, and help raise the question: does this method work correctly for urgent packages?
Here is an example of such a comment:
public class Parcel: IParcel{ ...
Very interesting community opinion on this issue. Thanks in advance for your comments.