/*********************************************** * CS2510 Spring 2011 * Lecture #13.2 * State Chang: single class ***********************************************/ /** Goal: change the phone number for a given person */ import tester.Tester; /* +-----------+ | ILoPerson |<------------+ +-----------+ | +-----------+ | / \ | --- | | | ------------------- | | | | +------------+ +----------------+ | | MTLoObject | | ConsLoObject | | +------------+ +----------------+ | +------------+ +-| Object first | | | | ILoObject rest |-+ | +----------------+ v +-------------+ | Person | +-------------+ | String name | | int phone | +-------------+ */ /* We decide to mutate the state of the Person object: The Person object remains the same, but gets a new phone number. When dealing with lists, we produce a new list with the mutated Person object with the changed phone number. We have to be careful to make sure that there is only one instance of a Person with this name and number. We also need to be careful when testing this program. The change of the Person's phone number is visible in every list that contains the mutated Person. Furthermore, unless we reset the Person object value to its original state, some of the earlier test could fail. We always want to make sure that any test in our test suite can run independently of other tests. It is not the case here - we need to do better. */ // to represent a person in a contacts list class Person{ String name; int phone; Person(String name, int phone){ this.name = name; this.phone = phone; } // EFFECT: // change the phone number of this person to the given one void newPhone(int newNumber){ this.phone = newNumber; } // is this the same person as the given person? boolean samePerson(Person that){ return this.name.equals(that.name) && this.phone == that.phone; } } // to represent a list of Person-s interface ILoPerson{ // to compute the size of this list public int size(); // is the given person in this list? public boolean contains (Person that); // produce a list with the phone number for the given person // changed to the given number public ILoPerson changePhone(String name, int phone); } // to represent an empty list of person contacts class MtLoPerson implements ILoPerson{ MtLoPerson() {} // compute the size of this empty list of persons public int size(){ return 0; } // does thsi empty list contain the given person? public boolean contains (Person that){ return false; } // produce a list with the phone number for the given person // changed to the given number public ILoPerson changePhone(String name, int phone){ throw new RuntimeException("No person with the given name in our list"); } } class ConsLoPerson implements ILoPerson{ Person first; ILoPerson rest; ConsLoPerson(Person first, ILoPerson rest){ this.first = first; this.rest = rest; } /* TEMPLATE: ... this.first ... -- Person ... this.rest ... -- ILoPerson ... this.rest.size() ... -- int ... this.rest.contains( Object ) ... -- boolean */ public int size(){ return 1 + this.rest.size(); } // does this list contain the given person? public boolean contains (Person that){ return this.first.samePerson(that) || this.rest.contains(that); } // produce a list with the phone number for the given person // changed to the given number public ILoPerson changePhone(String name, int phone){ if (this.first.name.equals(name)){ this.first.newPhone(phone); return this; } else return new ConsLoPerson(this.first, this.rest.changePhone(name, phone)); } } // Examples for a list of contacts class Examples{ Examples () {} Person bill = new Person("Bill", 2345); Person matt = new Person("Matt", 1234); Person anne = new Person("Anne", 7896); Person anne1 = new Person("Anne", 7896); Person john = new Person("John", 8866); Person john1 = this.john; Person jane = new Person("Jane", 3456); // test the method newPhone for the Person class void testNewPhoneV1(Tester t){ // setup Person jill = new Person("Jill", 3456); // run the test t.checkExpect(jill, new Person("Jill", 3456)); // run the method jill.newPhone(1234); // run the test t.checkExpect(jill, new Person("Jill", 1234)); // no need to reset - we only used local variables } // test the method newPhone for the Person class void testNewPhoneV2(Tester t){ // setup not needed, we use the data already defined // run the method this.jane.newPhone(1234); // run the test t.checkExpect(this.jane, new Person("Jane", 1234)); // reset this.jane = new Person("Jane", 3456); } ILoPerson mtphlist = new MtLoPerson(); ILoPerson family = new ConsLoPerson(this.bill, new ConsLoPerson(this.matt, new ConsLoPerson(this.anne, this.mtphlist))); ILoPerson familyNewAnne = new ConsLoPerson(this.bill, new ConsLoPerson(this.matt, new ConsLoPerson(new Person("Anne", 4444), this.mtphlist))); ILoPerson friends = new ConsLoPerson(this.anne, new ConsLoPerson(this.john, this.mtphlist)); ILoPerson friends2 = new ConsLoPerson(this.anne1, new ConsLoPerson(new Person("John", 8866), this.mtphlist)); ILoPerson friendsNewAnne = new ConsLoPerson(new Person("Anne", 4444), new ConsLoPerson(this.john, this.mtphlist)); ILoPerson work = new ConsLoPerson(this.john1, new ConsLoPerson(this.jane, this.mtphlist)); // test the method size for a list of contacts void testSize(Tester t){ t.checkExpect(this.mtphlist.size(), 0); t.checkExpect(this.family.size(), 3); } // test the method contains for a list of contacts void testContains(Tester t){ t.checkExpect(this.mtphlist.contains(this.bill), false); t.checkExpect(this.family.contains(this.matt), true); t.checkExpect(this.friends.contains(bill), false); t.checkExpect(this.family.contains(new Person("Anne", 7896)), true); } // test the method changePhone for a list of contacts // This is a bad test, as it impacts our data and changes it forever // It even affects the data that has not been involved in the test // Look what happened to 'this.friends' !!! void testChangePhone(Tester t){ t.checkExpect(this.family.changePhone("Anne", 4444), this.familyNewAnne); t.checkException( new RuntimeException("No person with the given name in our list"), this.work, "changePhone", "Anne", 4444); // and the family has the right number for Anne: t.checkExpect(this.family.contains(new Person("Anne", 7896)), false); t.checkExpect(this.family.contains(new Person("Anne", 4444)), true); // and friends know about it too t.checkExpect(this.friends.contains(new Person("Anne", 4444)), true); // but friends2 list had a different instance of Anne // and that one did not change t.checkExpect(this.friends2.contains(new Person("Anne", 4444)), false); } // test the method contains for a list of contacts // these test only succeed if the previous tests have changed 'anne' void testContains2(Tester t){ t.checkFail(this.friends, this.friends2); t.checkExpect(this.friends, this.friendsNewAnne); t.checkExpect(this.friends.contains(new Person("Anne", 4444)), true); } }