java 代码细节(Remove Assignments to Parameters)



这个观点来自《重构-----改善既有代码的设计》

The code assigns to a parameter.

02

Use a temporary variable instead.

03
int discount (int inputVal, int quantity, int yearToDate) {
    if (inputVal > 50) inputVal -= 2;
    ...
04
int discount (int inputVal, int quantity, int yearToDate) {
    int result = inputVal;
    if (inputVal > 50) result -= 2;
    ...

Motivation

05

First let me make sure we are clear on the phrase “assigns to a parameter.” This means that if you pass in an object named foo, in the parameter, assigning to the parameter means to change foo to refer to a different object. I have no problems with doing something to the object that was passed in; I do that all the time. I just object to changing foo to refer to another object entirely:

06
void aMethod(Object foo) {
    foo.modifyInSomeWay();           // that's OK
    foo = anotherObject;             // trouble and despair will follow you
    ...
07

The reason I don’t like this comes down to lack of clarity and to confusion between pass by value and pass by reference. Java uses pass by value exclusively (see later), and this discussion is based on that usage.

08

With pass by value, any change to the parameter is not reflected in the calling routine. Those who have used pass by reference will probably find this confusing.

09

The other area of confusion is within the body of the code itself. It is much clearer if you use only the parameter to represent what has been passed in, because that is a consistent usage.

10

In Java, don’t assign to parameters, and if you see code that does, apply Remove Assignments to Parameters.

11

Of course this rule does not necessarily apply to other languages that use output parameters, although even with these languages I prefer to use output parameters as little as possible.

Mechanics

12
  • Create a temporary variable for the parameter.

  • Replace all references to the parameter, made after the assignment, to the temporary variable.

  • Change the assignment to assign to the temporary variable.

  • Compile and test.

    If the semantics are call by reference, look in the calling method to see whether the parameter is used again afterward. Also see how many call by reference parameters are assigned to and used afterward in this method. Try to pass a single value back as the return value. If there is more than one, see whether you can turn the data clump into an object, or create separate methods.

Example

13

I start with the following simple routine:

14
int discount (int inputVal, int quantity, int yearToDate) {
    if (inputVal > 50) inputVal -= 2;
    if (quantity > 100) inputVal -= 1;
    if (yearToDate > 10000) inputVal -= 4;
    return inputVal;
}
15

Replacing with a temp leads to

16
int discount (int inputVal, int quantity, int yearToDate) {
    int result = inputVal;
    if (inputVal > 50) result -= 2;
    if (quantity > 100) result -= 1;
    if (yearToDate > 10000) result -= 4;
    return result;
}
17

You can enforce this convention with the final keyword:

18
int discount (final int inputVal, final int quantity, final int yearToDate) {
    int result = inputVal;
    if (inputVal > 50) result -= 2;
    if (quantity > 100) result -= 1;
    if (yearToDate > 10000) result -= 4;
    return result;
}
19

I admit that I don’t use final much, because I don’t find it helps much with clarity for short methods. I use it with a long method to help me see whether anything is changing the parameter.

Pass By Value in Java

20

Use of pass by value often is a source of confusion in Java. Java strictly uses pass by value in all places, thus the following program:

21
class Param {
    public static void main(String[] args) {
        int x = 5;
        triple(x);
        System.out.println ("x after triple: " + x);
    }
    private static void triple(int arg) {
        arg = arg * 3;
        System.out.println ("arg in triple: " + arg);
    }
}
22

produces the following output:

23
arg in triple: 15
x after triple: 5
24

The confusion exists with objects. Say I use a date, then this program:

25
class Param {
      public static void main(String[] args) {
          Date d1 = new Date ("1 Apr 98");
          nextDateUpdate(d1);
          System.out.println ("d1 after nextDay: " + d1);
 
          Date d2 = new Date ("1 Apr 98");
          nextDateReplace(d2);
          System.out.println ("d2 after nextDay: " + d2);
      }
      private static void nextDateUpdate (Date arg) {
          arg.setDate(arg.getDate() + 1);
          System.out.println ("arg in nextDay: " + arg);
      }
      private static void nextDateReplace (Date arg) {
          arg = new Date (arg.getYear(), arg.getMonth(), arg.getDate() + 1);
          System.out.println ("arg in nextDay: " + arg);
      }
}
26

It produces this output:

27
arg in nextDay: Thu Apr 02 00:00:00 EST 1998
d1 after nextDay: Thu Apr 02 00:00:00 EST 1998
arg in nextDay: Thu Apr 02 00:00:00 EST 1998
d2 after nextDay: Wed Apr 01 00:00:00 EST 1998
28

Essentially the object reference is passed by value. This allows me to modify the object but does not take into account the reassigning of the parameter.

29

Java 1.1 and later versions allow you to mark a parameter as final; this prevents assignment to the variable. It still allows you to modify the object the variable refers to. I always treat my parameters as final, but I confess I rarely mark them so in the parameter list.


你可能感兴趣的:(java 代码细节(Remove Assignments to Parameters))