设计模式之组合模式

提示:此篇文章与上一篇文章耦合度较高,请先观看我

合并的新店发现了新的问题

新店老板找到小明他们,说之前的方式的确让我们的服务员减少不少的工作,但是我们现在的各种菜单越来越多,每家一种菜单,就必须在服务员里添加一次,最关键的是,我们准备在某些菜单中加入一些子菜单,就是说菜单里除了菜品,可能还有菜单。

小明看了看主管,疑惑的问这样可以实现吗?主管说,当然可以,我们会用到一种新的模式,用了这种模式,新店遇到的两个问题都解决了!服务员只用知道一个菜单,而这个菜单里面既有菜品又有子菜单,小明说,那这不就成了一个树状结构的菜单了么,菜品属于叶节点,而子菜单属于组合节点,组合节点又可以出现新的叶节点和组合节点……

主管说,对,我们就是要实现这样一种结构,它的名字叫组合模式,我们现在来看看它强大的威力吧!

首先,我们需要定义一个抽象类,它是我们叶节点和组合节点的共同接口:

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();
    }
}

实现具体的一个菜品,也就是我们的也叶节点:

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());
    }
}

我们可以看到,对于菜品类,并没覆盖父类的add、remove、getChild方法,因为对于叶节点,它在没有向下的叶节点和组合节点了。

实现菜单项,也就是我们的组合节点,它包含了叶节点和组合节点,也就是又有菜品,又有子菜单:

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());
        System.out.println("---------------------");

        Iterator iterator = menuComponents.iterator();
        while (iterator.hasNext()) {
            MenuComponent menuComponent = (MenuComponent)iterator.next();
            menuComponent.print();
        }
    }
}

和上面一样,对于父类的一些方法,我们的菜单项也没有实现的,因为对于菜单项那些方法是没有意义。再比较一下print()方法,上面的叶节点,它是以明确的菜品,我们可以打印出它的一些信息,而对于组合节点,由于它只是一个菜单,我们要打印的不光是它的信息,我们还需要它里面包含的菜品,当然还有它里面包含的子菜单的菜品,和子菜单中子菜单的菜品……于是我们使用迭代器遍历它,然后再调用里面包含的菜单信息的打印方法(可能是菜单、也可能是菜品,如果是菜单会有递归)。

现在,服务员轻松了:

public class Waitress {
    MenuComponent allMenus;

    public Waitress(MenuComponent allMenus) {
        this.allMenus = allMenus;
    }

    public void printMenu() {
        allMenus.print();
    }
}

定义

允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。

组合模式

定义组合模式的迭代器

在组合模式中,定义一个迭代器似乎是有必要的,因为组合节点是一个集合,所以我们需要遍历它,在上面的实现中,我们已经使用了迭代器来打印,他只是在组合节点中来使用迭代来遍历组合节点中的集合,现在我们需要一个迭代器来访问组合模式中的所有元素,首先我们需要在叶节点和组合节点的实现类中加入一个方法,其他实现不变

叶节点加入:

public Iterator createIterator() {
    return new NullIterator();
}
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 Iterator createIterator() {
        return new NullIterator();
    }

    public void print() {
        System.out.print("  " + getName());
        if (isVegetarian()) {
            System.out.print("(v)");
        }
        System.out.println(", " + getPrice());
        System.out.println("     -- " + getDescription());
    }
}

组合节点加入:

public Iterator createIterator() {
    return new CompositeIterator(menuComponents.iterator());
}
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 Iterator createIterator() {
        return new CompositeIterator(menuComponents.iterator());
    }


    public void print() {
        System.out.print("\n" + getName());
        System.out.println(", " + getDescription());
        System.out.println("---------------------");

        Iterator iterator = menuComponents.iterator();
        while (iterator.hasNext()) {
            MenuComponent menuComponent = 
                (MenuComponent)iterator.next();
            menuComponent.print();
        }
    }
}

先看看叶节点中加入的方法返回的迭代器:

public class NullIterator implements Iterator {

    public Object next() {
        return null;
    }

    public boolean hasNext() {
        return false;
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}

很容易理解,由于叶节点不需要遍历,所以他的实现如上所示,重点是组合节点遍历的实现,我们直接上代码:

public class CompositeIterator implements Iterator {
    Stack stack = new Stack();

    public CompositeIterator(Iterator iterator) {
        stack.push(iterator);
    }

    public Object next() {
        if (hasNext()) {
            Iterator iterator = (Iterator) stack.peek();
            MenuComponent component = (MenuComponent) iterator.next();
            if (component instanceof Menu) {
                stack.push(component.createIterator());
            } 
            return component;
        } else {
            return null;
        }
    }

    public boolean hasNext() {
        if (stack.empty()) {
            return false;
        } else {
            Iterator iterator = (Iterator) stack.peek();
            if (!iterator.hasNext()) {
                stack.pop();
                return hasNext();
            } else {
                return true;
            }
        }
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}

这段代码我不会做具体的解释,因为比较抽象,有一种只可意会不可言传的感觉,相信大家能看懂。

坚持原创分享,您的支持将鼓励我不断前行!