⬆️ ⬇️

How two programmers baked bread





I have been working as a programmer for many years, during which, oddly enough, I always program something. And what an interesting thing I noticed: in the code I wrote a month ago, you always want to fix something a little. I want to change a lot in the code of six months ago, and the code written two or three years ago turns me into an emo: I want to cry and die. In this article I will describe two approaches. Thanks to the first, the architecture of the program turns out to be confusing, and the support is unnecessarily expensive, and the second is the KISS principle.



So, imagine that there are two programmers. One of them is smart, read a bunch of articles on Habré, knows the GoF directory by heart, and Fowler - in person. The other one does everything simply. The first will be called, for example, Boris N., and the second - Markus P. Of course, names are fictitious, and all coincidences with real people and programmers are random.

')

So, the project manager comes to both of them (if PM doesn’t go to programmers in your universe, call it something else, for example BA or lead , this will not change the essence) and says:

- Guys, we need to make bread.



That is how it was “made” without specifying the mode of production.



What will our programmers do?







Boris creates his first abstraction — the Product class, from which he inherits the Bread class, and instances of this class are instantiated by the ProductFactory class factory method - createProduct ().



Marcus does about the same. It creates the Bread class and the Manager class with the createBread () factory method.



While the difference is minimal. The project manager, having a little deeper understanding (it only seems to him, yes) to the customer’s needs, comes a second time and says:

“We need bread not just to be made, but baked in the oven.”



And at once it was impossible to say that bread is baked not in a vacuum, but in the oven? Well, what do programmers do?







Boris renames the ProductFactory class to Oven, and highlights the abstraction - AbstractOven. To make it quite beautiful, it renames the createProduct () method to bakeProduct (). Thus, for the first time, Boris performed the refactoring, using the “selection of abstraction”, and also implemented the “abstract factory” pattern exactly as it is described in the literature. Well done, Boris.



But Marcus does nothing. From his point of view, everything is so good. Well, maybe it's worth slightly changing the implementation of createBread ().



The phase of the moon changes, and the manager for the third time comes to the programmers. He says:

- We need to have different types of stoves.



Well, fair.







Boris, joyfully rubbing his hands, creates the three heirs of AbstractOven - ElectricOven, MicrowaveOven and GasOven. And he removes the class Oven for uselessness.



Marcus also makes changes to the program. It adds the integer parameter ovenType to the createBread method.



For the fourth time, the manager comes to the programmers. He just read one of the books in the “I Know the World” series. Interference of new information and PMBoK gave an unexpected result. The manager says:

“We need a gas stove to not have a stove without gas.”







Boris absolutely groundlessly believes that there can be only one source of gas. And for such cases, there is always our favorite template . It creates a lone GasSourceSingleton, and in order to reduce connectivity it injects it through the GasSource interface into GasOven. Hooray, he applied dependency injection through a setter!



Modest by nature, Marcus creates a real private gasLevel field in the Manager class. Naturally, it is necessary to slightly change the logic of the createBread method, but what can you do!



But a couple of days later, the manager comes for the fifth time, and, full of licking, says:

- We need the ovens to bake more pies (separately - with meat, separately - with cabbage), and cakes.



Programmers also want to eat, so they get to work.







Boris is already beginning to feel something like that, but he can no longer stop. How does the stove know what exactly she needs to cook? Obviously - she needs a cook. And Boris, not long (and maybe long) thinking, creates a class Cook. He will have a method for cooking that takes in the abstract oven - cook (owen: AbstractOwen): Product. After all, this is logical - the cook takes the oven and cooks with it. Then Boris creates several more heirs of the class Product - Cake and Pasty, and from Pasty he inherits MeatPasty and CabbagePasty. And then for each type of product creates a separate chef - BreadCook, PastyCook and CakeCook.



It seems still normal, but it took a lot more time than Markus, who simply added another integer parameter to the createBread method - breadType.



For the sixth time the manager comes. By the way, what he asks for now is not a requirement of the customer, it is his own initiative. But no one will know about it, right?

- We need bread, cakes and pies baked according to different recipes.







“Hmm,” says Boris, and recalls the “builder” pattern (along with the “free interface” , of course). He creates the Recipe class, and to him the RecipeBuilder builder. He introduces the recipe (EXTREMELY!) Into the stove with the setRecipe setter (recipe: Recipe).



And Marcus (you won’t believe) adds another integer parameter to the createBread recipe.



The most interesting, as always, occurs far from computers. Namely: the manager, for the first time after the start of development, meets with the customer and finally understands why the stove was needed. He (the manager) comes to the programmers for the seventh time and says:

- We need to be able to burn bricks in the furnace.







For Boris, this is the last meeting with the manager, but nevertheless, he is making the last changes in the architecture. He distinguishes the abstract class AbstractHeatingSmth — an abstract heating something. For him, he creates a HeatingFactory factory. From AbstractHeatingSmth, it inherits ProductOven and Furance. The latter has a factory makeBrick method that creates an instance of the Brick object. But nothing works. The reader is invited to independently find an error in the architecture.



Marcus, too, is not so smooth. He has to create the third (!) Class in a row. He calls it Brick, and adds the makeBrick method to his Manager.



Of course, you can argue that Marcus is creating Ad and Israel inside the createBread method, and this is actually the case. But with the help of the “template method” pattern, the disorder can be structured. And in the abundance of factories and abstractions to understand, well, a little more difficult.



The conclusions I want to draw are probably a bit predictable.



Boris's approach is good because almost every part of the system can be isolated and covered with tests. But the time to create such a number of classes will be indecently long, and each change in requirements will result in a cascading change in code. An attempt to make the architecture flexible, having foreseen the wishes of the customer, usually fails - the architecture bends at the wrong place. Indeed, as we know, “the world is not just more amazing than we imagine,”

he is more amazing than we can imagine. ” And, having received another change request, the programmer is convinced of this like no other.



Marcus’s approach, of course, does not allow the use of unit testing, but it does produce results much faster, and the changes are given with less blood. This approach is the fastest start that startups of all stripes want so much. And, oddly enough, in such a code is really easier to understand, because it is easier.



And to rewrite everything all over again, if that - it always has time.



Picture taken from here



UPD. Very pleased that the article received so many responses. Here , for example, the solution to this problem in Haskell, but on the List.



UPD.2 And here is the C # version. Who else?



UPD.3 Marcus Bread and YAGNI

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



All Articles