📜 ⬆️ ⬇️

Dense code and testing

This article is written in the wake of Steve Yegge's article. A portrait of a n00b . For me, the main message of the article sounded like this: less comments, fewer classes, fewer methods, more code. Make the code tighter, do not be zealous in modeling. I find it difficult to judge this vision of the world, and indeed the question of comments as such is more speculative. But what has become a steel needle in me is the problem of testing such a dense code.



To describe the problem, let's look at a small example. Suppose you have a server and commands come from outside. You need to write the processing logic of these commands. I would model this problem something like this:
')


Why so and so much: firstly, we adhere to the principle of Code to Interface , which allows us, for example, when testing a server to forge (mock) a command handler; secondly, by isolating the logic of each team in a separate class, we can easily test this logic. You might think, why don't we just make one Handler class and add public methods for each team and submit these methods to tests, why make a bunch of classes? But, in my opinion, the problem is that the function of the class in this case is somehow “smeared” by the class, in a sense, we open to the outside world the unnecessary details of the implementation of our handler. After all, all that the server needs to know is that this class can process any command, and not that it can process command 1, command 2 and command 3.

Now let's try to think about how this problem would be implemented in the form of a “dense code”. Of course, I can not speak and think for the author, a person with 20-25 years of experience. Perhaps he means something else. But in this context, for me, code compression is, for example, spike all handlers into one class. And even better in one method:

 public class Handler {
     public Result handle (Command command) {
         if (command == COMMAND1) {
             // perform logic 1
         } else if (command == COMMAND2) {
             // perform logic 2
         } 
         //.  .  .  .
         else {
             log.error ("don't know how to handle command" + command);
         }
         return result;
     }
 }


Or maybe not to carry out the processing of commands into a separate class at all? Just put them right in the server?

 public class Server {
     public void converse (Socket client) {
         int command;
         while ((command = client.getInputStream (). read ())! = -1) {
             if (command == COMMAND1) {
                 // perform logic 1
             } else if (command == COMMAND2) {
                 // perform logic 2
             } 
             //.  .  .  .
             else {
                 log.error ("don't know how to handle command" + command);
             }
         }
     }
 }


How do you intend to test it? Only integration tests can somehow save the father of Russian democracy. Perhaps the author meant something completely different? I would be glad if Habraguru was enlightened by me and other “teenagers” on this topic.

In multi-paradigm languages ​​like Lisp or Python, the situation is somewhat different. Functions in them tend to turn into multi-layered map-filter-reduce layers.
Here, it seems to me, there is not much point in separating each map, filter or reduce operation into a separate function. It is enough to use well-tested predicates instead of deranged lambdas, comment on the most important layers of map-filter-reduce, and also test the final functions well.

PS I’ll make a reservation that I fully admit the dense implementation in imperative languages ​​of some algorithms like sorting. Such algorithms as jugglers - there is no point in juggling with one ball, the meaning appears only when juggling with a set of balls (objects, variables, etc.) at the same time. Although here, too, it seems to me, there are options.

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


All Articles