"Head First Design Patterns" study notes - iterator pattern

Let us traverse the array of customers, stack, list, or hash table, you can not know the way we store the objects, we rely on today, the iterator pattern.

Case

We have two restaurants to merge operations, their dishes have in common, we both shop class needs a common menu properties, we create a separate class menu, as follows.

public class MenuItem {
    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 boolean isVegetarian() {
        return vegetarian;
    }

    public double getPrice() {
        return price;
    }
}

Now look at two store category.

public class DinerMenu {
    static final int MAX_ITEMS = 6;
    int numberOfItems = 0;
    MenuItem[] menuItems;

    public DinerMenu() {
        menuItems = new MenuItem[MAX_ITEMS];

        addItem("Vegetarian BLT",
                "(Fakin') Bacon with lettuce & tomato on whole wheat",
                true,
                2.99);
        addItem("BLT",
                "Bacon with lettuce & tomato on whole wheat",
                false,
                2.99);
        addItem("Soup of the day",
                "Soup of the day, with a side of potato salad",
                false,
                3.29);
        addItem("Hotdog",
                "A hot dog, with saurkraut, relish, onions, toped with cheese",
                false,
                3.05);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        if (numberOfItems >= MAX_ITEMS) {
            System.err.println("Sorry, menu is full! Can't add item to menu");
        } else {
            menuItems[numberOfItems] = menuItem;
            numberOfItems += 1;
        }
    }

    public MenuItem[] getMenuItems() {
        return menuItems;
    }
}

public class PancakeHouseMenu {
    ArrayList menuItems;

    public PancakeHouseMenu() {
        menuItems = new ArrayList();
        addItem("K&B's Pancake Breakfast",
                "Pancakes with scrambled eggs, and toast",
                true,
                2.99);
        addItem("Regular Pancake Breakfast",
                "Pancakes with fried eggs, sausage",
                false,
                2.99);
        addItem("Blueberry Pancakes",
                "Pancakes made with fresh blueberries",
                true,
                3.49);
        addItem("Waffles",
                "Waffles, with your choice of blueberries or strawberries",
                true,
                3.59);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        menuItems.add(menuItem);
    }

    public ArrayList getMenuItems() {
        return menuItems;
    }
}

Structure is similar, there is a menu properties, but is a collection of one array, add a few dishes, as well as a return to the menu of getter () method during initialization.

Next is the main event we have to operate, waitress class, there are methods to print menu.

public class Waitress {

    public void printMenu() {
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        DinerMenu dinerMenu = new DinerMenu();

        ArrayList houseMenuMenuItems = pancakeHouseMenu.getMenuItems();
        MenuItem[] dinerMenuMenuItems = dinerMenu.getMenuItems();

        // 循环遍历
        for (int i = 0; i < houseMenuMenuItems.size(); i++) {
            MenuItem menuItem = (MenuItem) houseMenuMenuItems.get(i);
            System.out.println(menuItem.getName() + " ");
            System.out.println(menuItem.getPrice() + " ");
            System.out.println(menuItem.getDescription());
        }

        for (int i = 0; i < dinerMenuMenuItems.length; i++) {
            MenuItem dinerMenuMenuItem = dinerMenuMenuItems[i];
            System.out.println(dinerMenuMenuItem.getName() + " ");
            System.out.println(dinerMenuMenuItem.getPrice() + " ");
            System.out.println(dinerMenuMenuItem.getDescription());
        }
    }
}

This is the easiest to use for loop iterates, if we have a new store to shop union, we must continue to increase for loop iterates In this method, it is very redundant repeat, we should change the part of the package, we will use iterator pattern to improve. First, create an iterator interface.

public interface Iterator {
    boolean hasNext();
    Object next();
}

Then create separate iterator class collections and arrays, they achieve the above methods, one is judged whether there is still an element hasNext () method returns the next one is the next element () method.

public class DinerMenuIterator implements Iterator {
    MenuItem[] items;
    int position = 0;

    public DinerMenuIterator(MenuItem[] items) {
        this.items = items;
    }

    @Override
    public boolean hasNext() {
        if (position >= items.length || items[position] == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public Object next() {
        MenuItem menuItem = items[position];
        position += 1;
        return menuItem;
    }
}

public class PancakeHouseMenuIterator implements Iterator {
    ArrayList items;
    int position = 0;

    public PancakeHouseMenuIterator(ArrayList items) {
        this.items = items;
    }

    @Override
    public boolean hasNext() {
        if (position >= items.size() || items.get(position) == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public Object next() {
        MenuItem menuItem = (MenuItem)items.get(position);
        position += 1;
        return menuItem;
    }
}

Then modify the two menus category, delete getMenuItems () method, because it would expose achieve our internal code, and then add a method returns an iterator, we traverse data through this iterator.

public class DinerMenu {
    static final int MAX_ITEMS = 6;
    int numberOfItems = 0;
    MenuItem[] menuItems;

    public DinerMenu() {
        menuItems = new MenuItem[MAX_ITEMS];

        addItem("Vegetarian BLT",
                "(Fakin') Bacon with lettuce & tomato on whole wheat",
                true,
                2.99);
        addItem("BLT",
                "Bacon with lettuce & tomato on whole wheat",
                false,
                2.99);
        addItem("Soup of the day",
                "Soup of the day, with a side of potato salad",
                false,
                3.29);
        addItem("Hotdog",
                "A hot dog, with saurkraut, relish, onions, toped with cheese",
                false,
                3.05);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        if (numberOfItems >= MAX_ITEMS) {
            System.err.println("Sorry, menu is full! Can't add item to menu");
        } else {
            menuItems[numberOfItems] = menuItem;
            numberOfItems += 1;
        }
    }

    public Iterator createIterator() {
        return new DinerMenuIterator(menuItems);
    }
}

public class PancakeHouseMenu {
    ArrayList menuItems;

    public PancakeHouseMenu() {
        menuItems = new ArrayList();
        addItem("K&B's Pancake Breakfast",
                "Pancakes with scrambled eggs, and toast",
                true,
                2.99);
        addItem("Regular Pancake Breakfast",
                "Pancakes with fried eggs, sausage",
                false,
                2.99);
        addItem("Blueberry Pancakes",
                "Pancakes made with fresh blueberries",
                true,
                3.49);
        addItem("Waffles",
                "Waffles, with your choice of blueberries or strawberries",
                true,
                3.59);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        menuItems.add(menuItem);
    }

    public Iterator creatorIterator() {
        return new PancakeHouseMenuIterator(menuItems);
    }
}

The last is our waitress class, and create a private method for data to traverse the use of iterators, and then modify the print menu, utilizing creatorIterator just the () method returns an iterator, with private methods traverse data.

public class Waitress {

    public void printMenu() {
        DinerMenu dinerMenu = new DinerMenu();
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        Iterator dinerIterator = dinerMenu.createIterator();
        Iterator pancakeIterator = pancakeHouseMenu.creatorIterator();
        System.out.println("MENU\n----\nBREAKFAST");
        printMenu(dinerIterator);
        System.out.println("\nLUNCH");
        printMenu(pancakeIterator);
    }

    private void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem)iterator.next();
            System.out.println(menuItem.getName() + " ");
            System.out.println(menuItem.getPrice() + " -- ");
            System.out.println(menuItem.getDescription());
        }
    }
}

We no longer need getMenuItem () method, deleted, replaced iterator object is to obtain, and then use the following printMenu (Iterator iterator) method.

We just need to give them two iterators, then each store increases a method that returns the iterator object on it. Our waitress was so easy expansion and maintenance, when the new store, the store only need to create an object, it returns an iterator, call printMenu (Iterator iterator) method.

This iterator allows us to decouple from the waitress achieve specific class, she does not need to know what data is used in the menu structure, as long as she can get to know iterators can. Iteration allows us to traverse each element in the polymerization, rather than having to implement traversal within the method, just let us have access to every element in data aggregation, we encapsulate traversal.

We continue to improve our design, in fact, ArrayList has provided a method for obtaining Iterator for us, so we modify PancakeHouseMenu of createIterator () method, first import java.util.Iterator, we use the iterator interface provided by Java.

public Iterator creatorIterator() {
        return menuItems.iterator();
    }

Here we modify DinerMenu, to meet the needs of java.util.Iterator interface, which requires modifications from the beginning DinerMenuIterator. Introducing java.util.Iterator, achieve remove () method.

public class DinerMenuIterator implements Iterator {
    MenuItem[] items;
    int position = 0;

    public DinerMenuIterator(MenuItem[] items) {
        this.items = items;
    }

    @Override
    public boolean hasNext() {
        if (position >= items.length || items[position] == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public Object next() {
        MenuItem menuItem = items[position];
        position += 1;
        return menuItem;
    }

    @Override
    public void remove() {
        if (position <= 0) {
            throw new IllegalStateException("You can't remove an item until you've done at least one next()");
        }
        if (items[position - 1] != null) {
            for (int i = position - 1; i < items.length - 1; i++) {
                items[i] = items[i+1];
            }
            items[items.length-1] = null;
        }
    }
}

DinerMenu then added in the introduction java.util.Iterator. We can also add a Menu interface make two restaurant menu class that implements this interface, convenient method we call the maid class, so we are oriented programming interface, reducing coupling and concrete realization of the maid class.

public interface Menu {
    public Iterator createIterator();
}

public class DinerMenu implements Menu {...}

public class PancakeHouseMenu implements Menu {...}

After implement this interface, we look at the maid class.

public class Waitress {

    public void printMenu() {
        Menu dinerMenu = new DinerMenu();
        Menu pancakeHouseMenu = new PancakeHouseMenu();
        Iterator dinerIterator = dinerMenu.createIterator();
        Iterator pancakeIterator = pancakeHouseMenu.createIterator();
        System.out.println("MENU\n----\nBREAKFAST");
        printMenu(dinerIterator);
        System.out.println("\nLUNCH");
        printMenu(pancakeIterator);
    }

    private void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem)iterator.next();
            System.out.println(menuItem.getName() + " ");
            System.out.println(menuItem.getPrice() + " -- ");
            System.out.println(menuItem.getDescription());
        }
    }
}

Import java.util.Iterator, use the Menu declared two menus category, rather than a concrete class, "for interface programming, rather than programming." We can reduce the dependence between the waitress and concrete classes.

Iterator pattern definition

Iterative mode provides a sequential method for each element in a polymeric object, without showing the inside thereof is exposed.

Iterator pattern allows us to traverse each element in the data set, and do not expose its data structure, and the responsibility to traverse the elements iterator, rather than aggregate objects, which makes the interface and implementation aggregation becomes more concise , but also allows it to be more focused on aggregation tasks, and ignore things that traverse data.

: "Single responsibility" design principle reason for a class should be only one cause change.

You should avoid changing the class, because it is easy to modify the code caused many potential errors. If a class has two reasons to change, then the probability of this kind of change will rise, and when it really has changed, both of which will be affected, so we follow this principle, only to assign a responsibility to a class.

We must learn to distinguish is the responsibility of design, it is very difficult to look at the behavior of a large group, then focus them together, even though they may belong to two or more different responsibilities, but we are working tirelessly inspection own design, as the system grows, the reason we always observe a class change if there is more than one.

New Cafe

Now we want to add a cafe into our system, then how to design it?

public class CafeMenu implements Menu {
    Hashtable menuItems = new Hashtable();

    public CafeMenu() {
        addItem("Veggie Burger and Air Fries",
                "Veggie burger on a whole wheat bun, lettuce, tomato, and fries",
                true,
                3.99);
        addItem("Soup of the day",
                "A cup of the soup of the day, with a side salad",
                false,
                3.69);
        addItem("Burrito",
                "A large burrito, with whole pinto beans, salsa, guacamole",
                true,
                4.29);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        menuItems.put(menuItem.getName(), menuItem);
    }

    @Override
    public Iterator createIterator() {
        return menuItems.values().iterator();
    }
}

This is the coffee menu class, and the first two classes are similar menus, menu interfaces are realized, returns an iterator. Then the waitress printMenu class () method to add a few lines, to create a menu like coffee, get an iterator, call printMenu (Iterator iterator) method through the data.

public void printMenu() {
        Menu dinerMenu = new DinerMenu();
        Menu pancakeHouseMenu = new PancakeHouseMenu();
        CafeMenu cafeMenu = new CafeMenu();
        Iterator dinerIterator = dinerMenu.createIterator();
        Iterator pancakeIterator = pancakeHouseMenu.createIterator();
        Iterator cafeMenuIterator = cafeMenu.createIterator();
        System.out.println("MENU\n----\nBREAKFAST");
        printMenu(dinerIterator);
        System.out.println("\nLUNCH");
        printMenu(pancakeIterator);
        System.out.println("\nDINNER");
        printMenu(cafeMenuIterator);
    }

This completes the purpose of adding a new menu, but there is a problem, the program calls printMenu times (), it is not pretty, and each newly added menu, but also open the waitress to add more class codes how can we modify the design? Look at the code:

public class Waitress {

    ArrayList menus;

    public Waitress(ArrayList menus) {
        this.menus = menus;
    }

    public void printMenu() {
        Iterator iterator = menus.iterator();
        while (iterator.hasNext()) {
            Menu next = (Menu) iterator.next();
            printMenu(next.createIterator());
        }
    }

    private void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem)iterator.next();
            System.out.println(menuItem.getName() + " ");
            System.out.println(menuItem.getPrice() + " -- ");
            System.out.println(menuItem.getDescription());
        }
    }
}

Make a menu class collection property, when we passed a set of debugging just to give a waitress Object menu, and then traverse the collection in printMenu () method allows us to code more simple.

Now they want followed by a dessert of "sub-menu." Somewhat similar to a tree structure, we can not assign the dessert menu to menu items array, so have to modify! Modify see next blog.

Published 26 original articles · won praise 2 · Views 2319

Guess you like

Origin blog.csdn.net/qq_42909545/article/details/105024998
Recommended