Now I am reading the book "Clean Code" and now I decided to check with developers with more experience than I do.

It says that you should strive to write unary functions (which contain one argument), and it is better to accept nothing at all, so that it is just the beautiful name of the function, which you can intuitively understand what it does.

But, if I'm not mistaken, this is contrary to the rules of encapsulation. I understand that all the variables that the function uses are defined as global variables, but then this function will depend strictly on the class in which it is located. And in which case it will be much more difficult to transfer it to another class.

p.65

"Function Arguments"

In the ideal case, the number of function arguments is zero (a null-ary function). The following are functions with one argument (unary) and two arguments (binary). Functions with three arguments (ternary) should be avoided if possible. The need for functions with more arguments (poly-arrays) must be supported by very high arguments — and still, it is better not to use such functions.

And further, if you read a couple of pages, it describes it more deeply and it all comes down to the point that functions should ideally be without arguments.

Or not so I argue?

  • four
    "it’s better not to take anything at all" ??? You reason like that. - Igor
  • 6
    any sensible thought can be brought to the point of absurdity - Igor
  • one
    But you can quote, so that it is not taken out of context? but it sounds somehow painfully doubtful ... - xhr
  • five
    Actually (and above all) the meaning of using functions in code structuring. Its splitting into levels and minimizing each of the parts. The result should be a code that is read by a person in a natural way, like a good book. Moreover, each part of the code, executing its subtask, must remain meaningful throughout the entire task. For a simple task (and each function should be such), some new information is obviously required (these are its arguments), since in OOP everything else is in the variables of the object. This is the context in which the function works. Therefore, most likely, the author is right. - avp 10:39 pm
  • one
    a more functional approach, using pure functions (functions without side effects) is more convenient because looking at the function call (name, parameter names, result name), you can immediately understand that as a result of the call it changes in the state of the object and you don’t have to dig into code for three methods in depth. Another Martin illustrates a chapter with a class that implements a method object, an instance of which exists only to execute one method once. In more long-lived objects, the fascination with the removal of arguments into fields can lead to fields that make sense only during the operation of a single method. - zRrr

4 answers 4

I agree with the author of the book - the fewer the arguments, the better. Firstly, it is easier to read the code, there is no confusion about which variable means, secondly, it is easier to write tests since the number of tests to cover all possible combinations of input data depends on the number of the latter. I think this is obvious. Another argument in favor of reducing the number of arguments is the purpose of the function. If you have a lot of input data, it means that most likely your function does too much and needs to be divided into several simpler functions. Here I also agree with @cpp_user that beautiful code may not be as fast as beautiful. The book says that in the ideal case the function should be nular (not have arguments) but this is more theory. The author acknowledges that you will always have to write unary or binary functions. You almost always need to pass in a function to get the result which is the 'function' of the source data.

So functions with input parameters are not bad, but you should be more careful in designing code and minimize the number of arguments.

I do not remember if this is reflected in the book or not, but avoid cases when all (several) arguments of the function are of the same type. This often leads to errors.

And yet, global variables are evil, a million books have been written about this.

  • 2
    Readability and testing is another powerful argument, and the phrase "If you have a lot of input data, it means that most likely your function does too much and it needs to be divided into several simpler functions ..." in general, you can count as a perfect short answer - also says Code Complete. - AseN

The fact of unaryness (or, more precisely, even atomicity) of functions and methods is one of those that is almost impossible to achieve in 100% of cases in practice, but which, indeed, you should always strive for.

The essence of this advice is not to stupidly globalize all parameters, but to arrive at a good architectural solution. Because if the function / method / procedure / whatever takes on input more than 3-5 parameters, then in most cases it beats the quality of the architecture and indicates obvious gaps in it.

Encapsulation. What is the main purpose of encapsulation? - to form a good abstraction, to interact with which you need to know about the details of its implementation as little as possible (perfectly - nothing).

Compare the following two sets:

MyDateClass{ private Date date; private Time time; /* 1 */ public MyDateClass(Date date, Time time){...} public Date GetDate(){ return date; } public Time GetTime(){ return time; } ... /* 2 */ public MyDateClass(uint timestamp){...} public String GetPrintable(){ return date+" : "+time; } public void Add(MyDateClass mdClass){ ... } ... } 

Which of the sets, in your opinion, discloses implementation details? The correct answer is: first, because it kills the meaning of encapsulation as such.

And the reason for this is the redundancy that appears as a result of the greater number of arguments of constructor 1.

Conclusion: the less parameters a subject takes as input, the better is encapsulation - the parameters reveal implementation details.

For a more objective understanding of the principles of designing functions / methods / ... I would advise you to read Chapter 7 of the Perfect Code "High-quality methods".

    Such a wish directly follows from the principle of the sole duty. The method, for good, should perform a single, most atomic, task. And I assure you, there are very few such tasks for which you will need more than two parameters.

    Where they are needed more, it makes sense to think about creating an object that has all the necessary properties / data and transmit it.

    Foolish, contrived example, but it reveals the essence perfectly.

    Suppose we need to increase the value of a variable by one.

    Option one. Pass two parameters to the function and assign the result to the variable.

     i=i+1 

    Option two. We pass one argument to the function with which we change the variable.

     i+=1 

    Option three, ideal for this atomic problem. We use the function without any arguments at all.

     i++ 

      From myself I can add: Write the functions so that it is as easy as possible to understand what it does - a good name, the first thing you need to pay attention to.

      Each function should perform a specific task only at one level of abstraction — do not interfere with low-level calculations and high-level code inside.

      If you need to pass a lot of parameters to it, it is easier to make a class argument - get a unary function.

      There is a bad Hindu practice that the function should contain one input - one output. At the same time, reading such functions, especially if they are more than 1 page is just awful - use forced exits (return).

      In general, try to make the function as simple as possible. It is very easy to fence off a ton of code, just like getting lost in it, but to solve the problem is simply much more difficult.

      Also consider the tasks that you are facing. If maximum speed is required, then calling a heap of functions inside other functions may adversely affect application performance, especially recursive functions or functions with long call chains.