The lion's share of programmers with a clear conscience will declare that they prefer to solve problems simply, guided first of all by common sense. That's just this "just" everyone has their own and usually different from others. After one long and unconstructive dispute with a colleague, I decided to state what I consider to be simple myself and why. This did not lead to immediate agreement, but made it possible to understand each other’s logic and minimize unnecessary discussions.
The peculiarities of the human brain are such that it badly stores and distinguishes more than 7-9 elements in one list with the optimum number of 1-3.
Hence the recommendation - ideally to have no more than three members in the interface, three parameters in the method, and so on, and not to allow an increase in their number over nine.
This criterion can be implemented by means of static analysis.
The most important thing is in the first two lines of any class.
As a result, we know about the class all that is needed to use it (name, parameters, inputs, outputs), even without looking at the remaining code.
Of course, this works subject to limitations:
And this criterion can be implemented by means of static analysis.
One type - one task. Contracts - to interfaces, implementations - to classes, algorithms - to methods!
Multitools are good only if there is nothing else at hand. Thanks to the first two criteria, writing such types is easy. Yes, this is the principle of sole responsibility (and separation of interfaces to the heap)
And here a static analysis will have to be trusted to a person.
Restrictions - the same legal part of the contract, as well as method signatures.
And therefore ...
Static analysis here can help only with recommendations on the use of simpler variants of types, if they are seen.
Maximum convenience for reuse.
Use interface inheritance, avoid implementation inheritance and get three sources of code reuse
Yes, this is the Liskov principle of substitution in the simplest and most practical way possible.
A static analyzer may report unwanted implementation inheritance.
Also, do not hide the types or methods "just in case." Everything should be public, except for the fact that it is necessary to hide them as implementation details.
Your types should be simpler for composition, decoration, adaptation, facade organization, etc., than for change. With previous criteria, this is easy to achieve.
This will allow as many changes as possible to add to the addition of new code instead of rewriting the current one, which will reduce the regression and the need to edit old tests as well.
A perfect example is interface extension methods that add functionality on the one hand and do not change the original contract and implementation on the other.
Yes, this is the principle of openness-closeness .
No, static analysis is of little help here.
The types of constructor parameters should speak as much as possible about how to use their meaning.
For example:
Alas, static analysis does little to help.
Small types are better than large ones, since even poorly designed or realized small types are much easier to fix, rewrite or delete compared to large ones.
A large number of small types is not in itself a fatal problem, since types are not a list, but a graph. In the head at the same time it is enough to keep the connections of the current type, and their number is limited by the first criterion.
Type dependencies between themselves must be limited
The goal is to maximally simplify the type dependency graph. This helps both when navigating through the code, and in determining the possible affect of certain changes.
Yes, this criterion includes the principle of dependency inversion .
Yes, static analysis can help.
Same as the ninth criterion, but for assemblies. Strongly simplifies life and speeds up the assembly, especially if you immediately design with its account. For contract assemblies, it is easier to maintain backward compatibility (published contracts do not change). Builds with implementations can be replaced entirely - they still do not directly depend on each other.
By means of static analysis, it is possible to prohibit some assemblies from referring to others.
All previous criteria may be violated if necessary optimization. But one thing, in my opinion, always works:
It is easier to optimize the correct code than to adjust the optimized one.
A written statement of my own ideas clarified a lot for me.
I would like to see your criteria for simplicity in the comments.
Mentioning static analysis means the possibility of implementation, and not its presence at the current moment.
Additions and criticism are traditionally welcome.
Source: https://habr.com/ru/post/312950/
All Articles