CSC223 2004F, Class 25: Pause for Breath Admin: * Sam Tang is late again. (I'm keeping track because he will incurr big penalties.) * Missing: Kat (late), Omondi, Chase (excused) * The disadvantage of regularly making outlines is that you know when I haven't prepared. * Questions on the take-home exam? * Bring them next week! Email them! Both! * Questions on the in-class exam? * Format of in-class exam: Closed book (one page of notes); Mostly "essay-style" questions; Some nasty multiple-choice questions * Advantageous to start the take-home first * Note: Graphics homework due November 12th Overview: * Discussion of last quiz /Quiz, Discussed/ * What is "explicit case analysis" mentioned in 5.12 and 5.13 and many other places in the text? * Perhaps: Using "introspection" to evaluate the type of an object and taking action based on that type * Perhaps: You have a function and you're given an object as a parameter. You make a decision based on the type of that object. public class Person public void consume(Food eatme) { if (eatme instanceof HotFudgeSunday) { weight += 5; mood++; } else if (eatme instanceof Celery) { mood--; } else if (eatme instanceof RatPoison) { dead = true; } else { throw new Exception("Sorry, I don't know about " + eatme); } // else } } // class Person Most object-oriented programmers don't like this style of programming. What is the alternative? Polymorphism. In this particular case, have Food include an eatenBy(Person p) method public class Person { public void consume(Food f) { f.eatenBy(this); } } public class HotFudgeSunday extends Food { public void eatenBy(Person p) { p.incrementWeight(5); p.incrementMood(1); } } public class RatPoison extends Food { public void eatenBy(Person p) { p.kill(); } } Why is this a useful or better strategy? * If you want to change the behavior of RatPoison, you really want to change the code for RatPoison, not Person. (Methods go with the most natural class.) * It "feels" more object-oriented * It makes it a lot easier to add new objects Problems: * What if you're using someone else's objects that don't provide the polymorphic method? * Subclass those objects to provide the polymorphic method * You can't use this technique, and you should criticize the person for not predicting your needs * There is no plagiarism in programming: Copy the code, add the method Disadvantages: * In this particular case, the code that affects Person is not in Person (callbacks are sometimes hard to do) * Alternative example public class Meal { // The contents of the meal Food[] stuff; // Determine the cost of the meal in cents public int cost() { int total = 0; for (int i = 0; i < stuff.length; i++) { if (stuff[i] instanceof HotFudgeSundae) { total += 449; } else if (stuff[i] instanceof Celery) { total += 1; } else if (stuff[i] instanceof Caviar) { total += 100000*((Cavier) stuff[i]).weightinounces; } ... } // for return total; } // cost() } * Why would this be a better solution than the polymorphic solution? * Different restaurants use different pricing models * However, you could still use polymorphism and inheritance in taht cases: public class PhoenixBurger extends Burger { public int cost() { ...} } public class McBurger extends Burger { // McBurger is likely a registered trademark // of the McDonald's corporation. It is used // without explicit permission. public int cost() { ... } } * CouchAnd says "This is all well and good when the burgers are really different, but what about 'Coke'" * Riel says "Avoid prolifieration of classes!" * Another solution? Make price a field of Food * Use an external table HashMap phoenixprices = new HashMap(); HashMap mcprices = new HashMap(); phoenixprices.put(Burger, 600); mcprices.put(Burger, 89); * That's why these are "heuristics" and not "hard and fast rules that you fail for violating" * Jesse ponderwed whether case statements based on integers are also explicit case analysis. Can we use polymorphism to solve that problem? No, but we can probably use the hash table trick. HashMap decisions = new HashMap(); decisions.put(new Integer(1), new Method(){ public void execute() { ... } }); decisions.put(new Integer(100), new Method(){ public void execute() { ... } }); ... // Here's the revised case statement ((Method) decisions.get(new Integer(i))).execute(); * Compare to switch (i) { case 1: ...; case 100: ...; } * The prior is much slower if you execute the test once. * In a naive implementation of switch statements, the prior is much faster if you execute the test many times * Smart implementations of switch statements do something similar anyway * What is "surreptitious coupling"? How does it relate to the other kinds of coupling? * When a class finds out the internal structure of another class by some means * What is nil coupling: X and Y don't deal with each other. * What is export coupling: X only uses public methods of Y * What is overt coupling: X has knowledge of the fields (implementation) of Y because Y has revealed it; X uses that knowledge * What is covert coupling: X has knowledge of the fields (implementation) of Y even though Y has not revealed it; X uses that knowledge * surruptitious = overt union covert (Riel seems to be one of the few people who use this term) Moral: Sometimes terms are just there to confuse you