Peter Bunus Department of Computer and Information Science Linköping University, Sweden peter.bunus@liu.se
TDDB84 Design Patterns Lecture 04 More on Iterators, Composite, - - PowerPoint PPT Presentation
TDDB84 Design Patterns Lecture 04 More on Iterators, Composite, - - PowerPoint PPT Presentation
TDDB84 Design Patterns Lecture 04 More on Iterators, Composite, Abstract Factory Peter Bunus Department of Computer and Information Science Linkping University, Sweden peter.bunus@liu.se The Constitution of Software Architects
2
The Constitution of Software Architects
Encapsulate what varies Program through an interface not to an implementation Favor Composition over Inheritance Classes should be open for extension but closed for modification Don’t call us, we’ll call you ????????? ????????? ????????? ?????????
3
Iterating through menus
printMenu()
- prints every item on the
menu printBreakfastMenu()
- print just breakfast items
printLunchMenu()
- print just lunch items
printVegetarianMenu()
- print all vegetarian menu
items isItemVegetarian()
- given the name of an item,
returns true is the item is vegetarian, otherwise returns false
4
What did we do?
We wanted to give to the Waiter an easy way to iterate over more items ... and we didn’t want him to know about how the menu items are implemented 1 2 3 4 ArrayList of MenuItems 1 2 3 4 An Array
- f
MenuItems Our menu item had two different implementations and two different interfaces for interacting
5 TDDB84 Design Patterns HT1 2009 LECTURE 04
We decoupled the Waiter
1 2 3 4 1 2 3 4
Iterator Iterator next() next()
So we gave the waiter an iterator for each group of
- bjects he needed to
iterate over... Now he doesn’t have to worry about which implementation we used; he always uses the same
- interface. He has been
decoupled from the implementation
6 TDDB84 Design Patterns HT1 2009 LECTURE 04
We Can Embrace Change
Iterator next() HashTable Iterator next() Vector Iterator next() LinkedList
Java collections that already have an Iterator implemented By giving him an Iterator we have decoupled him from the implementation
- f the menu items so we
can easily add new menus if we want.
7 TDDB84 Design Patterns HT1 2009 LECTURE 04
The Waitress Code
public class Waitress { Menu pancakeHouseMenu; Menu dinerMenu; Menu cafeMenu; public Waitress(Menu pancakeHouseMenu, Menu dinerMenu, Menu cafeMenu) { this.pancakeHouseMenu = pancakeHouseMenu; this.dinerMenu = dinerMenu; this.cafeMenu = cafeMenu; } public void printMenu() { Iterator pancakeIterator = pancakeHouseMenu.createIterator(); Iterator dinerIterator = dinerMenu.createIterator(); Iterator cafeIterator = cafeMenu.createIterator(); System.out.println("MENU\n----\nBREAKFAST"); printMenu(pancakeIterator); System.out.println("\nLUNCH"); printMenu(dinerIterator); System.out.println("\nDINNER"); printMenu(cafeIterator); } private void printMenu(Iterator iterator) { while (iterator.hasNext()) { MenuItem menuItem = (MenuItem)iterator.next(); System.out.print(menuItem.getName() + ", "); System.out.print(menuItem.getPrice() + " -- "); System.out.println(menuItem.getDescription()); } }
8
The Constitution of Software Architects
Encapsulate what varies Program through an interface not to an implementation Favor Composition over Inheritance Classes should be open for extension but closed for modification Don’t call us, we’ll call you ????????? ????????? ????????? ?????????
9
The Waitress Code Revised
public class Waitress { ArrayList menus; public Waitress(ArrayList menus) { this.menus = menus; } public void printMenu() { Iterator menuIterator = menus.iterator(); while(menuIterator.hasNext()) { Menu menu = (Menu)menuIterator.next(); printMenu(menu.createIterator()); } } void printMenu(Iterator iterator) { while (iterator.hasNext()) { MenuItem menuItem = (MenuItem)iterator.next(); System.out.print(menuItem.getName() + ", "); System.out.print(menuItem.getPrice() + " -- "); System.out.println(menuItem.getDescription()); } } } public class MenuTestDrive { public static void main(String args[]) { PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu(); DinerMenu dinerMenu = new DinerMenu(); Waitress waitress = new Waitress(pancakeHouseMenu, dinerMenu); waitress.printMenu(); waitress.printVegetarianMenu(); ... } ... }
10
Design Principle Ahead
Joe you could allow your aggregates to implement their internal collections and related
- peration AND the iteration
- methods. You could save some
classes here Joe don’t do that. I feel that bad things will happen in the future if you do that
11
Diner Menu Iterator
public class DinerMenuIterator implements Iterator { MenuItem[] items; int position = 0; public DinerMenuIterator(MenuItem[] items) { this.items = items; } public Object next() { MenuItem menuItem = items[position]; position = position + 1; return menuItem; } public boolean hasNext() { if (position >= items.length || items[position] == null) { return false; } else { return true; } } }
12 TDDB84 Design Patterns HT1 2009 LECTURE 04
The Diner Menu Implementation
public class DinerMenu { static final int MAX_ITEMS = 6; int numberOfItems = 0; MenuItem[] menuItems; int position=0; public DinerMenu() { menuItems = new MenuItem[MAX_ITEMS]; .... addItem("BLT","Bacon with lettuce & tomato on whole wheat", false, 2.99); ...... } public void addItem(String name, String description, boolean vegetarian, double price) { .... } } public Iterator createIterator() return new DinerMenuIterator(menuItems) public Object next() { MenuItem menuItem = menuItems[position]; position = position + 1; return menuItem; } public boolean hasNext() if (position >= menuItems.length || menuItems[position] == null) { return false; } else { return true; } } }
13 TDDB84 Design Patterns HT1 2009 LECTURE 04
The Star Trek Convention is in Town
Joe there is a Stat Trek Convention in Town and the Klingonians would like to visit our
- restaurant. According to the
Klingonian customs our waitress need to print them the menu backwards The Romulans are also coming in the evening. We need to give them some galactic snails, I sent you a new menu with galactic snail
- dishes. Print the menu as usual.
14
Design Principle
A class should have only one reason to change
15
The Constitution of Software Architects
Encapsulate what varies Program through an interface not to an implementation Favor Composition over Inheritance Classes should be open for extension but closed for modification
- Don’t call us, we’ll call you
- A Class should have only one reason to change
????????? ????????? ?????????
16
Violating the Single Responsability Principle
High Low reliability reusability testability understandability maintainability
Just when we thought it was safe...
Joe, we need to insert a dessert menu into the Dinner menu. The kids will love that. Could you please fix that?
18
The Desired Menu Structure
HashTable Coffee Menu 1 2 3 4 ArrayList Pancake Menu 1 2 3 4 Array Dinner Menu
1 2
3
ArrayList All Menus 1 2 3 4 Dessert Menu
All menus Pancake House Menu Dinner Menu Coffee Menu
MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem
Dessert Menu
MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem
What we need
- A tree structure to accomodate menus
- A way of traversing the tree
- Traversing in a flexible manner (e.g.
Traverse only the Diner’s dessert menu)
Peter Bunus Department of Computer and Information Science Linköping University, Sweden peter.bunus@liu.se
Composite
21
The Composite Pattern
- The Composite Pattern allows us to build structures of objects in the
form of tree that contains both composition of objects and individual
- bjects as nodes
22
The Composite Explained
The Client uses the Component interface to manipulate the objects in the commposition The Component defines an interface for all objects in the composition both the composite and the leaf node The Component may implement a default behavior for Add(), Remove(), GetChild() and its operations Note that Leaf will inherit Add(), Remove(), GetChild() which don’t make a lot of sense for a Leaf node A Leaf has no children A Leaf defines the behavior for the elements in the composition. It does this by implementing the the
- perations that the Composite
supports The Composite’s role is to define behavior
- f the components having children and to
store child components. It also implements Leaf related operations. Some of them might not make sense in Composite and exceptions might be generated
23
The Menu Composite
The Waitress is going to use the MenuComponent interface to access both Menus and MenuItems MenuComponent represents the interface for both MenuItem and Menu. Methods for manipulating the
- components. The
components are MenuItem and Menu MenuItem overrides the methods that make sense, an uses the default implementation in MenuComponent for those that don’t make sense Menu also overrides the methods that make sense, like a way to add and remove menu items.
24
The Menu Component
public abstract class MenuComponent { public void add(MenuComponent menuComponent) { throw new UnsupportedOperationException(); } public void remove(MenuComponent menuComponent) { throw new UnsupportedOperationException(); } public MenuComponent getChild(int i) { throw new UnsupportedOperationException(); } public String getName() { throw new UnsupportedOperationException(); } public String getDescription() { throw new UnsupportedOperationException(); } public double getPrice() { throw new UnsupportedOperationException(); } public boolean isVegetarian() { throw new UnsupportedOperationException(); } public void print() { throw new UnsupportedOperationException(); } }
Operations used by MenuItems The composite methods
25
The MenuItem
public class MenuItem extends MenuComponent { String name; String description; boolean vegetarian; double price; public MenuItem(String name, String description, boolean vegetarian, double price) { this.name = name; this.description = description; this.vegetarian = vegetarian; this.price = price; } public String getName() { return name; } public String getDescription() { return description; } public double getPrice() { return price; } public boolean isVegetarian() { return vegetarian; } public void print() { System.out.print(" " + getName()); if (isVegetarian()) { System.out.print("(v)");} System.out.println(", " + getPrice()); System.out.println(" -- " + getDescription());
} }
26 TDDB84 Design Patterns HT1 2009 LECTURE 04
The Menu
public class Menu extends MenuComponent { ArrayList menuComponents = new ArrayList(); String name; String description; public Menu(String name, String description) { this.name = name; this.description = description; } public void add(MenuComponent menuComponent) { menuComponents.add(menuComponent); } public void remove(MenuComponent menuComponent) { menuComponents.remove(menuComponent); } public MenuComponent getChild(int i) { return (MenuComponent)menuComponents.get(i);} public String getName() { return name;} public String getDescription() { return description;} public void print() { System.out.print("\n" + getName()); System.out.println(", " + getDescription()); Iterator iterator = menuComponents.iterator(); while (iterator.hasNext()) { MenuComponent menuComponent = (MenuComponent)iterator.next(); menuComponent.print(); } } }
27 TDDB84 Design Patterns HT1 2009 LECTURE 04
The Waitress Code
public class Waitress { ArrayList menus; public Waitress(ArrayList menus) { this.menus = menus; } public void printMenu() { Iterator menuIterator = menus.iterator(); while(menuIterator.hasNext()) { Menu menu = (Menu)menuIterator.next(); printMenu(menu.createIterator()); } } void printMenu(Iterator iterator) { while (iterator.hasNext()) { MenuItem menuItem = (MenuItem)iterator.next(); System.out.print(menuItem.getName() + ", "); System.out.print(menuItem.getPrice() + " -- "); System.out.println(menuItem.getDescription()); } } }
Before
public class Waitress { MenuComponent allMenus; public Waitress(MenuComponent allMenus) { this.allMenus = allMenus; } public void printMenu() { allMenus.print(); } }
After
28 TDDB84 Design Patterns HT1 2009 LECTURE 04
Runing the Restaurant
public class MenuTestDrive { public static void main(String args[]) { MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU", "Breakfast"); MenuComponent dinerMenu = new Menu("DINER MENU", "Lunch"); MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner"); MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course!"); MenuComponent coffeeMenu = new Menu("COFFEE MENU", "Stuff for the afternoon coffee"); MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined"); allMenus.add(pancakeHouseMenu); allMenus.add(dinerMenu); allMenus.add(cafeMenu); pancakeHouseMenu.add(new MenuItem( "K&B's Pancake Breakfast", "Pancakes with scrambled eggs, and toast", true, 2.99)); .... dinerMenu.add(new MenuItem( "Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99)); ..... dinerMenu.add(dessertMenu); dessertMenu.add(new MenuItem( "Apple Pie", "Apple pie with a flakey crust, topped with vanilla icecream", true, 1.59)); ..... cafeMenu.add(new MenuItem( "Veggie Burger and Air Fries", "Veggie burger on a whole wheat bun, lettuce, tomato, and fries", true, 3.99)); .... cafeMenu.add(coffeeMenu); coffeeMenu.add(new MenuItem( "Coffee Cake", "Crumbly cake topped with cinnamon and walnuts",true,1.59)); Waitress waitress = new Waitress(allMenus); waitress.printMenu(); } }
29
Runing the Restaurant
The Dessert Menu please The Pancake Menu please The Coffe Menu please
Peter, you said that I should respect the SINGLE RESPONSIBILITY PRINCIPLE. Now you are proposing me a Composite Pattern with classes with double responsibilities
SRP?
Yes indeed we are intentionally violating the SRP. Actually I’m not violating it; I’m trading it for transparency
- By allowing the Component Interface to
contain the child management operations and leaf operations, a client can treat both composite and leaf nodes uniformly
31
The Constitution of Software Architects
Encapsulate what varies Program through an interface not to an implementation Favor Composition over Inheritance Classes should be open for extension but closed for modification
- Don’t call us, we’ll call you
- A Class should have only one reason to change
????????? ????????? ?????????
32
Composite Pattern - Example
The Composite Design Pattern
34
The Abstract Factory
35 TDDB84 Design Patterns HT1 2009 LECTURE 04
Meanwhile at the Pizza Restaurant...
36
Baking Pizzas with the Factory Method
+prepare() +bake() +cut() +box() Pizza CheesePizza PepperoniPizza ClamPizza VeggiePizza
What we have learned from the Factory Mehod?
- First of all let’s take a look on what we tried to
avoid
public class PizzaStore { public Pizza createPizza(String style, String type){ Pizza pizza = null; if (style.equals(”Paris”)){ if (type.equals(”cheese”)){ pizza = new ParisStyleCheezePizza; } if (type.equals(”clam”)){ pizza = new ParisStyleClamPizza; } ..... } else if (style.equals(”Rome”)){ if (type.equals(”cheese”)){ pizza = new RomeStyleCheezePizza; } if (type.equals(”clam”)){ pizza = new RomeStyleClamPizza; } ..... } else { .... } } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza;
}
When you instantiate a class you are depending on the on its concrete class: if the implementation of the concrete classes changes we need to modify the pizza store
38 TDDB84 Design Patterns HT1 2009 LECTURE 04
Design Principle
Depend upon abstractions. Do not depend upon concrete classes.
39
The Constitution of Software Architects
Encapsulate what varies Program through an interface not to an implementation Favor Composition over Inheritance Classes should be open for extension but closed for modification
- Don’t call us, we’ll call you
- A Class should have only one reason to change
- Depend upon abstractions. Do not depend upon
concrete classes.
????????? ?????????
40
DIP explained
Depend upon abstractions. Do not depend upon concrete classes.
Peter could you please translate me the dependency Inversion Principle to English
High-level components should not depend on low level components; they should both depend on abstractions PizzaStore is ”high level component” Pizzas are ”low level components”
41
Guidlines to follow the DIP
- No variable should hold a reference to a concrete class
- If you use new you are holding a reference to a concrete class. Use a factory to get
around that
- No class should derive from a concrete class
- If you derive from a concrete class, you’re depending on a concrete class. Derive
from an abstraction like an interface or an abstract class.
- No method should override an implemented method of any of its base
classes
- If you override an implemented method, then you base class wasn’t really an
abstraction to start with. Those methods implemented in the base class are meant to be shared by all your subclasses
Pizza Ingredients
Building Ingredient Factories
public interface PizzaIngredientFactory { public Dough createDough(); public Sauce createSauce(); public Cheese createCheese(); public Veggies[] createVeggies(); public Pepperoni createPepperoni(); public Clams createClam(); }
TDDB84 Design Patterns HT1 2009 LECTURE 04
Building the NY Ingredient Factory
public class NYPizzaIngredientFactory implements PizzaIngredientFactory { public Dough createDough() { return new ThinCrustDough(); } public Sauce createSauce() { return new MarinaraSauce(); } public Cheese createCheese() { return new ReggianoCheese(); } public Veggies[] createVeggies() { Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() }; return veggies; } public Pepperoni createPepperoni() { return new SlicedPepperoni(); } public Clams createClam() { return new FreshClams(); } }
45 TDDB84 Design Patterns HT1 2009 LECTURE 04
Now it is time for Pizza
public abstract class Pizza { String name; Dough dough; Sauce sauce; Veggies veggies[]; Cheese cheese; Pepperoni pepperoni; Clams clam; abstract void prepare(); void bake() { System.out.println("Bake for 25 minutes at 350");} void cut() { System.out.println("Cutting the pizza into diagonal slices");} void box() { System.out.println("Place pizza in official PizzaStore box");} void setName(String name) { this.name = name; } String getName() { return name; } } public class CheesePizza extends Pizza { PizzaIngredientFactory ingredientFactory; public CheesePizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } void prepare() { System.out.println("Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); } }
46
The Pizza Store
public class NYPizzaStore extends PizzaStore { protected Pizza createPizza(String item) { Pizza pizza = null; PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory(); if (item.equals("cheese")) { pizza = new CheesePizza(ingredientFactory); pizza.setName("New York Style Cheese Pizza"); } else if (item.equals("veggie")) { pizza = new VeggiePizza(ingredientFactory); pizza.setName("New York Style Veggie Pizza"); } else if (item.equals("clam")) { pizza = new ClamPizza(ingredientFactory); pizza.setName("New York Style Clam Pizza"); } else if (item.equals("pepperoni")) { pizza = new PepperoniPizza(ingredientFactory); pizza.setName("New York Style Pepperoni Pizza"); } return pizza; } }
47 TDDB84 Design Patterns HT1 2009 LECTURE 04
Running the Pizzeria
public class PizzaTestDrive { public static void main(String[] args) { PizzaStore nyStore = new NYPizzaStore(); PizzaStore chicagoStore = new ChicagoPizzaStore(); Pizza pizza = nyStore.orderPizza("cheese"); System.out.println("Ethan ordered a " + pizza + "\n"); pizza = chicagoStore.orderPizza("cheese"); System.out.println("Joel ordered a " + pizza + "\n"); }
The Pizza Abstract Factory
The AbstractPizzaIngredientFactory is the interface that defines how to make a family of related products: everything we need to make a pizza The Clients of the AbstractFactory are the two instances of
- ur PizzaStore
NYPizzaStore and ChicagoStylePizza Store Each factory produces a different implementation for the family of products The job of the concrete pizza factories is to make pizza ingredients. Each factory knows how to create the right
- bject for their region
49
The Abstract Factory Template
- Provide an interface for creating families of related or dependent
- bjects without specifying their concrete classes.
50
Abstract Factory Example
- Interface toolkit to support multiple look-and-feel standards
51