Touching the void

The code for this part of the lecture can be found here.

Until now all methods produced a new instance of the object. the only exception has been the construction of circularly referential data, wehre we needed to change the state of the object after it has been created.

There are other situations where the value the object represents has to change after the object has been constructed. We call this mutation. We say that the methods that cause such change have side effects.

We will discuss the design of methods that cause side effects using as an example a list of phone numbers. Let's start with examples.

  Matt : Wendy 1234
         Dan   8768
         Pat   6284

  Dan  : Matt  7999
         Judy  1833
         Erna  3325

  Rob  : Wendy 1234
         Pat   6289
         Matt  7999
 

We'll need a phone entry that will hold the name and number of a person. We will also need a list of phone numbers that will keep a list of contacts for a person.

  
 
                     +----------------+
                     | IPhList        |
                     +----------------+
                             |
                            / \
                            ---
                             |
                -----------------------------
                |                           |
        +----------------+          +----------------+
        | MtPhList       |          | ConsPhList     |
        +----------------+          +----------------+
                                 +--| PhEntry first  |
                                 |  | IPhList  rest  |
                                 |  +----------------+
                                 |
                                 V
                        +----------------+
                        | PhEntry        |
                        +----------------+
                        | String  name   |
                        | int     num    |
                        +----------------+
                        
                        

Now, let's add the method changeNum to PhEntry.

...
  // create a new PhEntry with the same name and num as the phone number.
  public PhEntry changeNum(int num){
    return new PhEntry(this.name, num); 
  }

Consider the scenario where we have the following lists of phone numbers.

    IPhList mattlist = new ConsPhList(wendy, 
                                     new ConsPhList(dan, 
                                                    new ConsPhList(pat,mt))); 


    IPhList danlist = new ConsPhList(matt, 
                                    new ConsPhList(judy, 
                                                   new ConsPhList(erna,mt))); 

    IPhList roblist = new ConsPhList(wendy, 
                                    new ConsPhList(pat, 
                                                   new ConsPhList(matt,mt))); 

Wendy's phone number has changed and we would like to have this change reflected in our example. Using updateNum creates a new instance with the name Wendy and the new number, e.g. PhEntry wendy2 = wendy.changeNum(4321);. This does not update the previous instance for wendy in the two lists mattlist and roblist. We would like to have the update to wendy's instance be visible to the lists that have wendy as a member. The effect of the method should be the change of Wendy's phone number everywhere where her phone record is known.

    // EFFECT: update the phone number for this PhEntry
    public void changeNum2(int num){ 
      this.num = num;
  }

The return type of our method is void, that is, it returns nothing back. The execution of this method is used for its side effects. It mutates the state of the instance by updating the field num.

We now have a problem. We cannot test this method by comparing the value it produces with the expected value. Instead, we can only observe and test the efect of the method on an existing instance of the class. Therefore our tests will consist of three parts:

Typically the three steps are composed into a test method, or the setup part is shared by several test methods.