Review ====== 1) What are design patterns? 2) Which design patterns have we seen so far? - In class: - state (behavioral) - singleton (creational) - observer (behavioral) - iterator (behavioral) - Last class: - composite (structural) - builder (creational) - factory (creational) - flyweight (structural) A bit more about design patterns: visitors and singletons ========================================================= 1) What will be printed by the program below? public class Knife { public void isBoughtBy(Avatar a) { a.buy(this); } } ---//--- public class Sword extends Knife { public void isBoughtBy(Avatar a) { a.buy(this); } } ---//--- public class Avatar { public void buy(Knife k) { System.out.println("Avatar bought a knife"); } public void buy(Sword s) { System.out.println("Avatar bought a sword"); } } ---//--- public class SpiderAv extends Avatar { public void buy(Knife k) { System.out.println("Spider bought a knife"); } public void buy(Sword s) { System.out.println("Spider bought a sword"); } public static void main(String args[]) { Avatar a1 = new Avatar(); Avatar a2 = new SpiderAv(); SpiderAv sa = new SpiderAv(); Knife k = new Knife(); Sword s = new Sword(); Knife ks = new Sword(); System.out.println("a1"); a1.buy(k); a1.buy(s); System.out.println("a2"); a2.buy(k); a2.buy(s); System.out.println("sa"); sa.buy(k); sa.buy(s); } } * Java and C++ implement single dispatch: a method is called based on the dynamic type of the callee, not the dynamic type of the arguments. 2) Why does java use single dispatch? 3) And now: what will be printed? public class Knife { public void isBoughtBy(Avatar a) { a.buy(this); } } --//-- public class Sword extends Knife { public void isBoughtBy(Avatar a) { a.buy(this); } } --//-- public static void main(String args[]) { Avatar a1 = new Avatar(); Avatar a2 = new SpiderAv(); SpiderAv sa = new SpiderAv(); Knife k = new Knife(); Sword s = new Sword(); Knife ks = new Sword(); System.out.println("And what about?"); ks.isBoughtBy(a1); ks.isBoughtBy(a2); ks.isBoughtBy(sa); } 4) Draw an UML class diagram for the program in slide 4: 5) Implement a program to print the description of all the components of a composite element. 6) What are the advantages and problems of implementing the last question directly on the Item? 7) Implement a program to evaluate the value of a composite item, according to a particular table. 8) What problems (7) and (5) have in common? 8.1) How to reuse this common structure? 9) How to traverse the data-structure? public interface Item { ... void accept(Visitor v); } --//-- public class SimpleItem implements Item { ... public void accept(Visitor v) { v.visit(this); } } --//-- public class Ruby extends SimpleItem { ... public void accept(Visitor v) { v.visit(this); } } --//-- public class Diamond extends SimpleItem { ... public void accept(Visitor v) { v.visit(this); } } 10) How the accept of the CompositeItem would look like? public class CompositeItem extends SimpleItem { ... private List components; public void accept(Visitor v) { for (Item i : components) { i.accept(v); } } } 11) How the implementation of a visitor look like? public interface Visitor { void visit(SimpleItem si); void visit(CompositeItem ci); void visit(Chain c); void visit(Diamond d); void visit(Ruby r); } 12) How would an implementation of the printer visitor be like? public class DescriptiveVisitor implements Visitor { public void visit(Chain c) { System.out.println("Chain " + c.getDescription()); } public void visit(Diamond d) { System.out.println("Diamond " + d.getDescription()); } public void visit(Ruby r) { System.out.println("Ruby " + r.getDescription()); } public void visit(SimpleItem si) { System.out.println("Unknown " + si.getDescription()); } public void visit(CompositeItem ci) { System.out.println("Composite " + ci.getDescription()); } } 12) What would happen if I remove the accept from Ruby? What would be printed? 13) Implement a program to evaluate the price of an item: public class ComputeValueVisitor implements Visitor { private int value; public void visit(Chain c) { value += 1000; } public int getValue() { return value; } public void visit(Diamond d) { value += 2000; } public void visit(Ruby r) { value += 1600; } public void visit(SimpleItem si) { value += si.getValue(); } public void visit(CompositeItem ci) { } } 14) Implement a visitor to obtain the lowest HP in an item: public class LowestHPVisitor implements Visitor { int hp = Integer.MAX_VALUE; public int getLowestHP() { return hp; } private void checkHP(Item i) { System.out.println("visiting " + i.getName()); if (i.getHitPoints() < hp) { hp = i.getHitPoints(); } } public void visit(SimpleItem si) { checkHP(si); } public void visit(CompositeItem ci) { checkHP(ci); } public void visit(Chain c) { checkHP(c); } public void visit(Diamond d) { checkHP(d); } public void visit(Ruby r) { checkHP(r); } } 15) Consider the factory that we have implemented in the last class: import composite.Item; public class Driver { public static void main(String[] args) { Item treasure = null; // Using the simple builder factory: try { treasure = (new factory.builder.BuilderFactory()).getBuilder("treasure").getItem(); } catch (factory.builder.NoBuilderException e) { e.printStackTrace(); } System.out.println(treasure); } } We have to instantiate the factory every time we want a builder. How to change the code to guarantee that we have only one instance of the factory? public class BuilderFactory { private static final BuilderFactory instance = new BuilderFactory(); private BuilderFactory() { } public static BuilderFactory getInstance() { return instance; } public Builder getBuilder(String bName) throws NoBuilderException { if (bName.equals("necklace")) { return new NecklaceBuilder(); } else if (bName.equals("treasure")) { return new TreasureBuilder(); } else { throw new NoBuilderException(bName + " is not a valid builder"); } } }