📜 ⬆️ ⬇️

Why I do not teach SOLID and the "principle of eliminating dependencies"

Article 1. Why I do not teach SOLID


If you are talking to someone who is not indifferent to the quality of the code, SOLID is an abbreviation soon enough in a conversation that helps developers remember five important principles of object-oriented programming:

SOLID is useful. It was developed by experts in our field. He helps people talk about design. Helps create systems that are resistant to change.

Previously, SOLID was the cornerstone of my design toolkit. I did my best to make my code as SOLID as possible. I taught others to do the same.

Today SOLID remains important to me, but I no longer try to make my SOLID code. I rarely mention it when talking about design. And the more I do not teach developers to use it, who want to learn good design methods of design. He is no longer under my arm in my “toolbox”. He lies in a dusty box in the attic. I keep it because it is important, but I rarely use it.

SOLID creates problems. Big enough problems to take him away to the attic. When I promoted SOLID, those who were against its use pointed to these problems. I neglected the opinion of these people, calling them skeptics who resist change, or who are not interested in the quality of the code. Now I was convinced that they were right from the start.
')
If I were asked to describe all these problems in one word, I would choose the word incomprehensible . Developers using SOLID (including myself) often create incomprehensible code. This SOLID code has a low level of connectivity, it is easy to test. But it is incomprehensible. And often not at all as adaptable as the developer would like.

The main problem is that SOLID concentrates on dependencies. Each of the principles of openness / closedness, interface separation and dependency inversion leads to the use of a large number of dependencies on abstractions (an interface or an abstract C # / Java class). The principle of openness / closeness uses abstractions for ease of expansion. The principle of separation of the interface contributes to the creation of more customer-oriented abstractions. The principle of dependency inversion says that dependencies should be on abstraction, not on a specific implementation.

As a result, all this leads to the fact that developers are beginning to create interfaces anywhere. They clutter up their code with interfaces like IFooer, IDoer, IMooer, IPooer.

Navigation turns into a nightmare. During code review it is often not clear which part of the code will work. But it normal. This is SOLID. This is a great design!

To help deal with this insanity, we are implementing an IoC container. And also a mock framework for tests. If before everything was not too clear, now it becomes incomprehensible completely. Now you in the most direct sense cannot find the constructor call in the code. Trying to figure it all out? Good luck! But it is nothing. Because this is SOLID. This is a great design!

Since this is such a great design, why is it so incomprehensible? Is this a great design if developers can't simply and clearly explain it? I do not think so. SOLID is important and useful. But I do not think that developers are good at it. That's exactly why I no longer teach SOLID.

Article 2. Beyond the SOLID: "Dependency Elimination Principle"



Think of dependencies as visible problems in the code.

According to the principle of eliminating dependencies, we change our default mood and begin to perceive dependencies as visible problems in the code. This does not mean that we will have no dependencies at all. It simply means that our default attitude is to perceive them as problems. A new attitude will force us to grasp what we need for each dependency, and eliminate it.

Do we get any data through dependency? Transfer the data yourself, but remove the Recipient Data.
Do we pass something through addiction? Consider a model with events and a subscription to them, instead of passing an interface. Alternatively, use a Data Transfer Object that describes the dependency status and react to its change (as, for example, in MVVM).

Value objects

In most projects, there are too many dependencies, because they use little value objects to describe their concepts. The following few simple tips will help you create a clear and understandable code with a moderate number of dependencies and abstractions (peeped in the article “Take on the 4 Rules of Simple Design” of JB Rainsberger)



By applying these simple principles, you will be shocked how quickly your code will become simple, readable, testable and extensible, but without unnecessary cluttering up the interfaces.

Testability as a reference

Let's take a look at the principle of eliminating dependencies in terms of testability. Code with dependencies (even if these dependencies are minor) is more difficult to test than code without them. Here are the levels that I arranged in ascending order from easy to hard (I think I found it in some other blog post, but for the life of me I can't find it):



Level 0 test is elementary. Simply send various input data to a function and check the correctness of the result.

Level 1 is not too different from Level 0, except that you have a few more methods to test it and a few more configurations that also need to be tested. Level 1 is good because it forces you to encapsulate your concepts in Value Objects.

Level 2 is more difficult than Level 1, as you have to think about the internal state and test different cases in which it changes. But sometimes you need Level 2 because of its advantages.

Level 3 is the hardest to test. You have to either use Mock objects or test several modules at the same time.

I want to make testing as simple as possible, so I try to use as low a level as possible that meets my requirements: I mainly use Level 0 and Level 1 code. Sometimes Level 2 and rarely Level 3. My code becomes very functional, however takes advantage of object-orientation when creating Value Objects for grouping related functionality.

Coming back to SOLID

Suppose we have applied the principle of eliminating dependencies. Let's analyze how SOLID this code has become:



It's all about the principle of a single duty

Applying the principle of eliminating dependencies, you focus exclusively on the principle of a single duty. And you get the flexibility of the principle of openness / closeness, which leads to ease of adaptation to business requirements. And you can forget about all the difficulties that carry with them the principles of Liskov substitution, Interface separation and Inversion dependencies. We win on all counts!

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


All Articles