1. Introduction
Faced with the need to control the work of other programmers, you begin to understand that, besides the things that people learn quite easily and quickly, there are problems that require significant time to be eliminated.
Relatively quickly, you can train a person to use the necessary tools and documentation, correct communication with the customer and within the team, proper goal setting and prioritization (well, of course, to the extent that you own it yourself).
But when it comes to the code itself, everything becomes much less straightforward. Yes, you can point out weak points, you can even explain what is wrong with them. And next time, get a review with a completely new set of problems.
')
Professions of the programmer, like most other professions, have to learn every day for several years, and, by and large, and all my life. First, you master a set of basic knowledge in the amount of N semester courses, then you trample on various rakes for a long time, learn from the experience of older friends, learn good and bad examples (for some reason, bad ones more often).
Speaking of basic knowledge, it should be noted that the ability to write beautiful professional code is something that, for one reason or another, is absolutely
not part of this basic knowledge. Instead, in relevant institutions, as well as in books, we are told about algorithms, languages, OOP principles, design patterns ...
Yes, all this is necessary to know. But at the same time, an understanding of how a decent code should look, usually appears already in the presence of practical (often more or less negative) experience. And provided that life βpokedβ you not only into succulent samples of bad code, but also into examples that are seriously worthy of imitation.
This is where the complexity lies: your idea of ββa βdecentβ and βbeautifulβ code is completely based on many years of personal experience. Now try to transfer this presentation in a short time to a person with a completely different experience or even completely without it.
But if the quality of the code written by people working with us is really important for us, itβs still worth a try!
2. Why do we need beautiful code?
Usually, when we are working on a specific software product, the aesthetic qualities of the code do not concern us in the first place.
We are much more important than our performance, the quality of the implementation of the functional, the stability of its work, the possibility of modification and expansion, etc.
But are the aesthetic qualities of the code a factor positively affecting the above indicators?
My answer: yes, and at the same time, one of the most important!
This is so because a beautiful code, regardless of the subjective interpretation of the concept of beauty, has the following (to some extent reducible to each other) essential qualities:
- Readability. Those. an opportunity, looking at the code, to quickly understand the implemented algorithm, and evaluate how the program will behave in a particular case.
- Controllability. Those. the possibility of making the required corrections to the code in the shortest time possible, while avoiding various unpleasant, predictable and unpredictable consequences.
Why these qualities are really important, and how they contribute to the improvement of the indicators indicated at the beginning of the paragraph, I am sure, obviously, to anyone who is engaged in programming.
And now, in order to move from common words to specifics, let's make a reverse move and say that it is readable and manageable code that we usually perceive as beautiful and professionally written. Accordingly, in discussing how to achieve these qualities, we will further focus.
3. Three basic principles.
Turning to the presentation of my own experience, I note that, while working on the readability and controllability of my own and someone else's code, I gradually came to the following understanding.
Regardless of the specific programming language and the tasks to be solved, in order for the code fragment to have these two qualities sufficiently, it is necessary that it be:
- as linear as possible;
- short;
- self-documented.
You can endlessly list various hints and techniques with which you can make the code more beautiful. But I affirm that the best, or, in any case, sufficiently good results can be achieved by focusing on these three principles.
Therefore, I will further try to explain in detail their essence, as well as describe a set of basic techniques with which you can bring your code into line with these principles.
4. Linearization code.
It seems to me that of the three basic principles, it is linearity that is the most non-obvious, and it is precisely it that is most often neglected.
Probably, because over the years of study (and, possibly, scientific activity), we are used to discuss the nature of non-linear algorithms with
O (n3) ,
O (nlogn) , etc.
All this, of course, is good and important, but when talking about the implementation of business logic in real projects, one usually has to deal with completely different algorithms, more like illustrations of children's programming books. Something like this (taken from
Google ):

Thus, I associate with linearity not so much the asymptotic complexity of the algorithm, but the
maximum number of nested blocks of code nested in one another, or the level of nesting of the maximum length of a sub-section of the code .
For example, a perfectly linear fragment:
do_a()
do_b()
do_c()
:
do_a();
if (check) {
something();
} else {
anything();
if (whatever()) {
for (a in b) {
if (good(a)) {
something();
}
}
}
}
ββ .
: , , C-like , , . , ( Java Javascript).
4.1. 1. .
.
β β :
- .
- .
- .
- .
- , .
- .
.
listen_client();
if (!is_clean()) {
...
}
check_cost();
if (!client_agree()) {
...
}
find_defects();
if (defects_found()) {
...
}
create_request();
take_money();
bye();
, , :
listen_client();
if (!is_clean()) {
...
}
check_cost();
if (client_agree()) {
find_defects();
if (defects_found()) {
...
}
create_request();
take_money();
} else {
...
}
bye();
, , .
4.2. 2. break, continue, return throw, else.
:
...
if (!client_agree()) {
...
} else {
find_defects();
if (defects_found()) {
...
}
create_request();
take_money();
bye();
}
:
...
if (!client_agree()) {
...
return;
}
find_defects();
if (defects_found()) {
...
}
create_request();
take_money();
bye();
, ,
else. -,
break,
continue,
return throw ( ). -, , ,
else , - .
-,
return , - ( : - ).
( ) , .
4.3. 3. .
.. β β , .
ββ :
:
do_a()
if (check) {
something();
} else {
anything();
if (whatever()) {
for (a in b) {
if (good(a)) {
something();
}
}
}
}
:
procedure do_on_whatever()
}
}
do_a();
if (check) else
}
, , , , . , .
,
, , . (.
. 6.1).
4.4. 4. , , , , .
:
if (check) {
do_a();
something();
if (whatever()) {
for (a in b) {
if (good(a)) {
something();
}
}
}
} else {
do_a();
anything();
if (whatever()) {
for (a in b) {
if (good(a)) {
something();
}
}
}
}
:
do_a();
if (check) {
something();
} else {
anything();
}
if (whatever()) {
for (a in b) {
if (good(a)) {
something();
}
}
}
4.5. 5 ( ). try...catch , .
,
try...catch , , .. , , .
, . .. , , . : ,
try..catch, .
, , , . , , .
4.6. 6. if-.
. :
if (a) {
if (b) {
do_something();
}
}
:
if (a && b) {
do_something();
}
4.7. 7. (a? b: c) if.
:
if (a) {
var1 = b;
} else {
var1 = c;
}
:
var1 = a ? b : c;
, , .
:
if (a) {
var1 = b;
} else if (aa) {
var1 = c;
} else {
var1 = d;
}
:
var1 = a ? b :
aa ? c : d;
, , .
,
var1 , (
. 6.8).
4.8. , .
listen_client();
if (!is_clean()) {
wash();
}
check_cost();
if (!client_agree()) {
pay_for_wash();
bye();
return;
}
find_defects();
if (defects_found()) {
say_about_defects();
if (!client_agree()) {
pay_for_wash_and_dyagnosis();
bye();
return;
}
}
create_request();
take_money();
bye();
, , 3
bye() , , , , return (, return).
try...finally, , .. . .
( ,
. 5.1 , ):
procedure negotiate_with_client()
find_defects();
if (defects_found())
}
create_request();
take_money();
}
listen_client();
if (!is_clean())
negotiate_with_client();
bye();
, - , , , . , β¦
5. .
, , , , .
, β , , , . , , , , . .
5.1. 1. .
, , , - . , , , .
, .
. , : . . , - , .
,
. 4.3.
if. , :
procedure proc1()
procedure proc2()
proc1();
proc2();
:
init()
do1()
do2()
. :
a = new Object()
init(a)
do(a)
b = new Object()
init(b)
do(b)
:
procedure proc(a)
proc(new Object());
proc(new Object());
5.2. 2. .
β , . , , .
, ,
null. ,
NPE .
:
obj = new Object();
...
if (obj != null) {
obj.call();
}
, . (C , ,
new() null. ( .. Java) ).
( ) .
, , :
obj = factory.getObject();
obj.call1();
if (obj != null) {
obj.call2();
}
, .
, , :
procedure proc1(obj)
procedure proc2(obj)
obj = factory.getObject();
if (is_valid(obj)
, , . , (,
proc1() proc2() API), .
:
- , . . Java, , ββ @Nullable. , , / null.
- ! , . , , . , .
, , - , , .
NullObject, , , ββ
null. null - .
null-safe ,
apache-commons Java. ,
null.
5.3. 3. ββ. .
β : , , . .
, , , 2D long-polling Javascript. , , , .
, , - , , . , , ββ -.
, , , ββ . , , , , .
, , , (,
apache-commons guava Java) .
5.4. 4. , .
ββ , ; , ; , , β , , , , .
,
, , .
, , , , , .
, , , , ,
.
5.5. 5. .
, : , , ..
, , Javascript.
:
if (obj != null && obj != undefined && obj.s != null && obj.s != undefined && obj.s != '') {
}
, β - β. , Javascript, :
if (obj && obj.s) {
}
, boolean,
if (obj) {} :
, , . , , , - ββ .
, :
if (!a) {
a = defaultValue;
}
a = a || defaultValue;
, .
6. .
ββ , XML JSON. , , , .
XML , , , , , , .
, ββ , , , .
ββ , . , , , , .
6.1. 1. , . , .
, β
.
name, , , , .
compare(), , , - 1000 .
NetworkDevice, , , .
, , , . , , .
, , . , , ,
.
, , ββ.
,
i,
j,
k,
s. .
i,
j, . , , foreach .
ii,
i1,
ijk42,
asdsa ., . , β¦ , - .
6.2. 2. , β -.
, β . , , , , . , , . , .
ββ β // . .
: .
6.3. 3. β β. , .
. 5.4., , , , , , , ( ) .
: , .
ββ :
...
int sum = counSum();
int increasedSum = sum + 1;
operate(increasedSum);
...
,
increasedSum , .. ,
(sum + 1) , . (ββ ):
...
int sum = counSum();
operate(sum + 1);
...
, :
...
operate(countSum() + 1);
...
β ,
.
, , . :
double descr = b * b - 4 * a *c;
double x1 = -b + sqrt(descr) / (2 * a);
descr , .. , , , , β β .
, , : //, . ,
. 6.1, , . , , // , .
6.4. 4. , β .
: , . . . , - .
, , .
:
if (i == abs(i)) {
}
:
if (i >= 0) {
}
, , .
6.5. 5. , private (protected), . β .
.
, . , β β, . . , , . , , , .
6.6. 6. ( ) .
, .
:
Object someobj = createSomeObj();
if (some_check()) {
} else {
someobj.call();
}
, someobj , . . , .
, - :
if (some_check()) {
} else {
Object someobj = createSomeObj();
someobj.call();
}
, ,
. 6.3:
if (some_check()) {
} else {
createSomeObj().call();
}
, . , . :
Object someobj = createSomeObj();
for (int i = 0; i < 10; i++) {
someobj.call();
}
createSomeObj() β , , .
, , .
6.7. 7. . , , .
β , .
, . β .
, , .
6.8. 8. .
, . .
, , , - .. , , .
, ,
apache CollectionUtils guava Collections2 Java
foreach β .
:
...
Collection<String> lowercaseStrings = new ArrayList<String>();
for (String s : somestrings) {
lowercaseStrings.add(StringUtils.lowerCase(s));
}
c:
...
Collection<String> lowercaseStrings = Collections2.transform(somestrings, new Function<String, String>() {
@Override
public String apply(String s) {
return StringUtils.lowerCase(s);
}
}));
Java 8, :
...
Collection<String> lowercaseStrings = somestrings.stream()
.map( StringUtils::lowerCase ).collect(Collectors.toList());
, .
finally catch (, - ). ,
try,
try.
6.9. 9. , .
, Java .
Javascript ( :
http://habrahabr.ru/post/154105).
:
var str = "mentioned by";
for(var i =0; l= tweeps.length; i < l; ++i){
str += tweeps[i].name;
if(i< tweeps.length-1) {str += ", "}
}
c:
var str = "mentioned by " + tweeps.map(function(t){
return t.name;
}).join(", ");
, β¦ .
6.10. 10. , , .
, . , :
- .
- , (βcomments lieβ).
- , .
- .
, . .. , . , , .
// , (
. 5.2).
7. .
, , :
. β .
. .
, , , , (, , ), . . , .
, : , , ( ). .
, , (, , ).
, , . , .
, , . , .
, β . ββ , , , . . .
, , ( , ) , IDE.
. . , .
!