📜 ⬆️ ⬇️

Wrong understanding of the principle of DRY


I know what you thought: “Another boring DRY article? We have them a little, or what? ".


Perhaps you `re right. But I meet too many developers (junior and senior) who use DRY as if they are hunting witches. Either completely unpredictable, or wherever possible. So, apparently, on the Internet there will never be enough articles about the principle of DRY.


If anyone does not know: the principle of DRY is "Don't Repeat Yourself" (do not repeat), and it was first mentioned in the book The Pragmatic Programmer . Although this principle in itself was known and applied long before the appearance of the book, however, it gave it a name and a precise definition.


So, without further ado, let's go on a trip to the wonderful country of DRY!


Do not repeat your knowledge


Although the phrase "do not repeat" sounds very simple, at the same time, it is too general.


In The Pragmatic Programmer, the principle of DRY is defined as "Every piece of knowledge must have a single, consistent and authoritative view within the system . "


Everything is great, but ... what is a "piece of knowledge"?


I would define it as any part of a business domain or algorithm.


If you take popular analogies from e-commerce, then the shipment class and its behavior will be part of the business area of your application. Shipment is the actual process your company uses to send goods to its customers. This is part of the company's business model .


Consequently, the logic of the work class shipment should appear in the application once.


The reason is obvious: let's say you need to send the goods to the warehouse. To do this, you will have to activate the shipping logic in 76 different locations of the application. No problem: insert logic 76 times. After some time, the boss comes in and asks for something to change in logic: instead of one warehouse, the company now distributes goods to three warehouses. As a result, you will spend a lot of time changing the logic, since you have to do it in 76 places! Time wasted for nothing, a good source of bugs, and a great way to beat the bosses.


Decision? Create a single representation of your knowledge . Put the logic of sending goods to the warehouse somewhere, and then use the presentation of this knowledge wherever necessary. In OOP, shipping can be a shipment class method that you can use to your liking.


Another example: let's say you write a tricky class for parsing B-trees . It can also be considered knowledge : it is an algorithm that must be determined once. And already the representation of knowledge can be used wherever you wish without repeating the knowledge itself.


DRY and code duplication


So does DRY belong to knowledge ? To business logic ?


Let's start with the obvious:


 <?php interface Product { public function displayPrice(); } class PlasticDuck implements Product { /** @var int */ private $price; public function __construct(int $price) { $this->price = $price; } public function displayPrice() { echo sprintf("The price of this plastic duck is %d euros!", $this->price); } } $plasticDuck = new PlasticDuck(2); $plasticDuck->displayPrice(); 

This code looks pretty good, right?


However, Lyokha, your fellow developer, will not agree with you. When Lyokha sees this code, he will come up to your table and will be indignant:


  1. The word price repeated 6 times.
  2. The displayPrice() method is repeated in the interface, in the implementation, and called during runtime.

Lyokha is a novice expert in your company and has no idea about OOP.


I just see how you, a wonderful developer, look at Lyokha, like an experienced gardener at a bot, and answer:


  1. This is a variable (and a property), and you need to repeat it in code.
  2. However, the logic (price display) is presented only once, in the method itself. Here neither knowledge nor algorithm is repeated.

There is no violation of the principle of DRY. Lech shuts up, overwhelmed by your aura of greatness that fills the room. But you questioned his knowledge, and he is angry. Lech google prinyp DRY, finds another example of your code, returns and pokes your face:


 <?php class CsvValidation { public function validateProduct(array $product) { if (!isset($product['color'])) { throw new \Exception('Import fail: the product attribute color is missing'); } if (!isset($product['size'])) { throw new \Exception('Import fail: the product attribute size is missing'); } if (!isset($product['type'])) { throw new \Exception('Import fail: the product attribute type is missing'); } } } 

Lyokha triumphantly exclaims: “You are a deer! This code is not DRY compliant! ” And you, having read this article, answer: "But business logic and knowledge still do not repeat!"


You are right again. The method checks the result of parsing CSV in only one place ( validateProduct() ). This is knowledge, and it is not repeated.


But Lech is not ready to accept defeat. “What about all these if ? Is this not an obvious DRY violation? ” You control your voice, carefully utter every word, and your wisdom is reflected from the walls, creating an echo of awareness: “Well ... no. This is not a violation. I would call it an unnecessary duplication of code , and not a violation of the DRY principle .


You type the code at lightning speed:


 <?php class CsvValidation { private $productAttributes = [ 'color', 'size', 'type', ]; public function validateProduct(array $product) { foreach ($this->productAttributes as $attribute) { if (!isset($product[$attribute])) { throw new \Exception(sprintf('Import fail: the product attribute %s is missing', $attribute)); } } } } 

Looks better, right?


Summarize:


  1. Duplication of knowledge is always a violation of the DRY principle. Although you can imagine a situation where it is not ( write in the comments, if you also have such examples ).
  2. Code duplication is not necessarily a violation of the DRY principle.

But Lyokha is not yet convinced. With the tranquility of a higher spiritual mentor, you put an end to the argument: “Many people think that DRY prohibits duplicate code. But it is not. The essence of DRY is much deeper. ”


Who said that? Dave Thomas, one of the authors of The Pragmatic Programmer, a book that defines the very principle of DRY!


We apply DRY everywhere: a recipe for disaster


Useless abstractions


Take a more vital, interesting example. I am currently working on an application for a film company. Movies and metadata (titles, descriptions, titles ...) can be easily downloaded into it. All this information is then displayed in the VoD platform . The application is built on the basis of the MVC pattern , and it looks like this:



But not only film crews can create content with this application. For example, the content management team in my company can use it. Why? Some film crews don't want to do this. The film crew and the content management team have completely different needs. The second work with CMS , and the first - no.


Therefore, we decided to create two interfaces. One for the content management team, without hints and explanations, but allowing you to add content very quickly. The second interface for the film crew, explains everything that needs to be done, very friendly. Here is the result:



It looks like an obvious and ugly violation of the DRY principle: the views and the controller are repeated.


Other solutions? It would be possible to group repeating logic with the help of a kind of abstraction . But then I would have to combine the controllers. If the abstraction changes, then each controller will need to support this change. We knew that in most cases, depending on use, these views would be displayed differently. It would be necessary to create a bunch of if in the actions of the controllers, but we did not want this. The code would be more complicated .


Moreover, controllers should not contain business logic. If you remember the definition of DRY, then this is just knowledge that should not be duplicated.


In other words, if you try to apply DRY everywhere, you can come to one of two things:


  1. Unnecessary union .
  2. Unnecessary complexity .

Obviously, neither one nor the other smiles at you.


Premature optimization


You should not use DRY if your business logic does not yet contain any duplications. Of course, everything depends on the situation, but, as practice shows, if you apply DRY to something that is used only once, you can be in a situation of premature optimization .


If you start abstracting something, because “you may need it later,” then don't do it. Why?


  1. You will spend time abstracting what may then never be claimed. Business needs can change very quickly and very much.
  2. I repeat, you can add complexity and unification to the code for ... pshik.

Code reuse and code duplication are two different things. DRY says that you do not need to duplicate knowledge, and not that you should always make the code reusable.


First write the code, debug it, and then keep all these principles in mind (DRY, SOLID and others) in order to effectively refactor the code. It is necessary to work with violation of the DRY principle when knowledge is already duplicated.


Duplication of knowledge?


Remember, I said above that the repetition of business logic is always a violation of DRY? Obviously, this is true for situations where the same business logic is repeated.


Example:


 <?php /** Shipment from the warehouse to the customer */ class Shipment { public $deliveryTime = 4; //in days public function calculateDeliveryDay(): DateTime { return new \DateTime("now +{$this->deliveryTime} day"); } } /** Order return of a customer */ class OrderReturn { public $returnLimit = 4; //in days public function calculateLastReturnDay(): DateTime { return new \DateTime("now +{$this->returnLimit} day"); } } 

You already hear your colleague Lech gently yelling into your ear: “This is an obvious violation of everything I believe in! But what about the principle of DRY? My heart is contracting! ”


But Lyokha is wrong again. From the point of view of e-commerce, the delivery time of the goods to the buyer ( Shipment::calculateDeliveryDay() ) is not related to the time of return of the goods by the buyer ( Return::calculateLastReturnDay ).


These are two different functionalities. What looks like duplication of code is actually a pure coincidence. What happens if you combine the two methods into one? If your company decides that the buyer can now return the goods within a month, then you will have to divide the method again. After all, if this is not done, the delivery time will also be one month!


And this is not the best way to win customer loyalty.


DRY is not just a principle for pedants.



Today, even gin can be DRY!


DRY is not something you should respect only in your code. No need to repeat knowledge in every aspect of your project. I’ll quote Dave Thomas again: “The concept of“ knowledge ”in the system covers not only code. It also applies to database schemas, test plans, an assembly system, and even documentation. ”


The essence of DRY is simple: you do not need to update several things in parallel if some kind of change is made.


If your knowledge is repeated twice in the code, and you forgot to update one view, then bugs will arise. This will lead to misconceptions and misunderstandings in the documentation, and to a completely erroneous implementation. And so on and so forth.


DRY is a principle


At the beginning of my career, I often suffered from analytical paralysis . All these principles did not allow me to be productive and efficient. It was too difficult, and I did not want to spoil everything.


I wanted to follow every letter of these principles to be a good developer .


However, principles are not rules. They are just tools to help go in the right direction.


In development, everything has a price. And DRY is no exception. Obviously, you should not repeat business logic everywhere, but you also don’t need to combine everything closely, saving on abstractions. You need to look for a balance that depends on the current situation.


')

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


All Articles