Side effects in Java methods

This might be a trivial question, but I need some clarification... There is a book called Clean Code that says that our methods should be small, preferably up to 5-10 lines long. In order to achieve that we need to split our methods into smaller ones. For instance, we may have someMethod() shown below. Let's say, modification of 'Example' takes 5 lines and I decide to move it into a separate method, modify 'Example' there and return it back to someMethod(). By doing this, someMethod() becomes smaller and easier to read. That's good, but there is a thing called "side effects" which says that we shouldn't pass an object to another method and modify it there. At least, I was told that it's a bad idea ) But I haven't seen anything prohibiting me from doing so in Clean Code.

public Example someMethod() {

    // ... different lines here

    Example example = new Example();
    example = doSomethingHere(example, param1, param2, ...);    

    // ... different lines here

    return example;
}

private Example doSomethingHere(Example example, 'some additional params here') {

    // ... modify example's fields here ...

    return example;
}

So, am I allowed to split the methods this way or such a side effect is prohibited and instead I should deal with a rather long-line method that definitely breaks Clean Code's rules talking about short methods?


UPDATED (more specific name for the sub-method)

public Example someMethod() {

    // ... different lines here

    Example example = new Example();
    example = setExampleFields(example, param1, param2, ...);    

    // ... different lines here

    return example;
}

private Example setExampleFields(Example example, 'some additional params here') {

    // ... modify example's fields here ...

    return example;
}

As JB Nizet commented, it's not actually a side effect if it's the only effect, so any blanket statement that "all side effects are bad" doesn't apply here.

Still, the main question stands: Is this (side) effect okay?

Talking about the principles first, side effects are, in general, dangerous for two reasons:

  • they make concurrency more difficult
  • they obscure/hide information
  • In your example, there is some information that is hidden. You could call this a potential side effect, and it can be exposed with a question: "Does this doSomethingHere method create a new object or modify the one I pass in?" The answer is important, and even more so if it's a public method. The answer should be trivial to find by reading the doSomethingHere method, especially if you're keeping your methods 'clean', but the information is nonetheless hidden/obscured.

    In this specific case, I would make doSomethingHere return void. That way there's no potential for people to think that you've created a new object. This is just a personal approach - I'm sure that plenty of developers say you should return the object you modify. Alternatively, you can pick a 'good' method name. "modifyExampleInPlace" or "changeSomeFieldsInPlace" are pretty safe names for your specific example, imo.


    we shouldn't pass an object to another method and modify it there.

    Who says that? That is actually a good practice in order to split your function in a way that forms a "recipe" and have specific functions that know exactly how to populate your object properly.
    What is not recommended (and probably the source where you got your recommendation misunderstood this rule) is defining a public API and modify the arguments. Users appreciate not having their arguments modified as it leads to less surprises. An example of that is passing arrays as arguments to methods.


    When you define an object and pass it to an other method, method itself can modify the content of the object therein which may be unwanted in some cases. This is because you pass the reference(shallow copy) of the object to that method and method can modify that object.

    For example when you pass an Array, Arrays are objects, to a method, method can change the content of the Array which may not be what the caller method expects.

    public static void main(String[] args){
      int[] arr= {1,2,3,4};
      y(arr);
      //After the method arr is changed
    }
    public void y(int[] comingArray){
      comingArray[0] = 10;
    }
    

    To make sure the values of Array cannot be changed, deep copy of the Array should be sent to method which is another story
    However this is not the case when you use primite types(int, float etc.)

    public static void main(String[] args){
      int a= 1
      y(a);
      //After the method a is not changed
    }
    public void y(int comingInt){
      comingInt = 5;
    }
    

    Due to the nature of the Objects, you should be careful
    To learn more about shallow copy and deep copy https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm

    链接地址: http://www.djcxy.com/p/8858.html

    上一篇: 接口vs基类

    下一篇: Java方法中的副作用