组合模式

1.定义

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

把整体和局部都当作一个对象,这就需要继承相同的类或者实现相同的接口,我们沿着这个思路实现代码.

2.代码实现

以   https://www.cnblogs.com/lishuaiqi/p/11291839.html  这个例子继续举例.

如果我们除了三个菜单, 早餐,午餐,晚餐这三种菜单,还需要拓展子菜单,也就是需要在晚餐后加上饭后甜点菜单,或者在甜点菜单加上饮品菜单等等之类的,这就形成了一个树形结构,如果继续用迭代器模式的话就有点不适合了,因为类型太多,还有子类型,需要增加很多不必要的判断.

我们用组合加上迭代器来实现这个功能

首先定义组合类,因为我们需要把 菜单 (组合) 和 菜单项(叶节点:没有孩子节点的节点)  都当成组合

package composite;

import java.util.Iterator;

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

都是基本操作,增加,删除,获取子节点等.还有获取一些元素,打印等功能,最后一个方法是抽象方法 createIterator  因为我们需要把遍历操作使用迭代器模式封装到迭代器中实现.

我们实现菜单,继承组合抽象类

package composite;

import java.util.ArrayList;
import java.util.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;
    }
    @Override
    public void add(MenuComponent menuComponent) {
        menuComponents.add(menuComponent);
    }
    @Override
    public void remove(MenuComponent menuComponent) {
        menuComponents.remove(menuComponent);
    }
    @Override
    public MenuComponent getChild(int i) {
        return (MenuComponent) menuComponents.get(i);
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public String getDescription() {
        return description;
    }
    @Override
    public void print() {
        System.out.print(" 
 "+getName());
        System.out.println(" , "+getDescription());
        System.out.println(" ---------------- ");
        
        Iterator iterator = menuComponents.iterator();
        while (iterator.hasNext()) {
            MenuComponent menuComponent = (MenuComponent)iterator.next();
            menuComponent.print();
        }
        
    }
    @Override
    public Iterator createIterator() {
        return new CompositeIterator(menuComponents.iterator());
    }
    
    
}

因为菜单只有名字和描述属性,我们只定义这两种变量,并实现相应的方法,迭代器后面会补上.在print方法中,通过遍历list的itreator来获取root根节点下的所有空间信息,因为组合和叶节点都实现了print 方法,我们可以直接调用print方法即可.

接下来定义菜单包含的内容---菜单项,因为没有子节点,所以也叫叶节点

package composite;

import java.util.Iterator;

public class MenuItem extends MenuComponent{
    String name;
    String description;
    boolean Vegetarian;
    double price;
    public MenuItem(String name, String description, boolean vegetarian, double price) {
        super();
        this.name = name;
        this.description = description;
        Vegetarian = vegetarian;
        this.price = price;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public boolean isVegetarian() {
        return Vegetarian;
    }
    public void setVegetarian(boolean vegetarian) {
        Vegetarian = vegetarian;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    
    //组合模式
    @Override
    public void print() {
        System.out.print(" " + getName());
        if (isVegetarian()) {
            System.out.print("(V)");
        }
        System.out.println( " , " + getPrice());
        System.out.println("  --" + getDescription());
    }
    @Override
    public Iterator createIterator() {
        return new NullIterator();
    }
    
    
    
}

这边四大属性都有,名字,描述,是否是素食,价格都有,所以我们的服务员最后要打印的也就是这个类的信息.

因为这个模块是使用了迭代器来遍历元素,所以我们定义组合的迭代器

package composite;

import java.util.Iterator;
import java.util.Stack;

public class CompositeIterator implements Iterator {
    Stack stack = new Stack();
    
    public CompositeIterator(Iterator iterator) {
        stack.push(iterator);
    }

    @Override
    //这个方法会判断下一个子节点是否有子节点
    public boolean hasNext() {
        if (stack.isEmpty()) {
            //空栈则返回false
            return false;
        } else {
            //获取第一个元素,peek不会删除元素
            Iterator iterator = (Iterator) stack.peek();
            if (!iterator.hasNext()) {//如果元素没有下一个
                //出栈并且递归调用,pop会删除第一个元素
                //通过pop我们把没有子节点的元素都给删除了
                stack.pop();
                return hasNext();
            } else {
                //有下个元素则返回true
                return true;
            }
        }
    }

    @Override
    public Object next() {
        if (hasNext()) {
            //如果有子节点则取出栈的第一个元素
            Iterator iterator = (Iterator) stack.peek();
            //因为上面的pop操作,所以我们第一个元素肯定是有子节点的
            MenuComponent component = (MenuComponent) iterator.next();
            //只需要判断是那种类型即可,如果是菜单,则需要添加进栈中,因为我们只需要获取菜单项
            if (component instanceof Menu) {
                stack.push(component.createIterator());
            }
            return component;
        }
        return null;
    }
    
    


}

这个功能有点复杂,解释都写在注释中了,这个就是遍历菜单项的,不包括菜单.

这边还有一个空的迭代器

package composite;

import java.util.Iterator;

public class NullIterator implements Iterator{

    @Override
    public boolean hasNext() {
        return false;
    }

    @Override
    public Object next() {
        return null;
    }

}

其实就是一直让hasNext为false就可以了,主要给MenuItem菜单项实现,因为菜单项就是子元素,没有子节点,所以给它返回 CompositeIterator  也没有意义.

还是服务员

package composite;

import java.util.Iterator;

public class Waitress {
    MenuComponent allMenus;
    
    public Waitress(MenuComponent allMenus) {
        this.allMenus = allMenus;
    }
    
    public void printMenu() {
        allMenus.print();
    }
    
    public void printVegetarianMenu() {
        Iterator iterator = allMenus.createIterator();
        System.out.println("-------------VEGETRAIAN MENU--------------");
        while (iterator.hasNext()) {
            MenuComponent menuComponent = (MenuComponent)iterator.next();
            try {
                if (menuComponent.isVegetarian()) {
                    menuComponent.print();
                }
            } catch (Exception e) {
            }
        }
                
    }
}

printVegetarianMenu 这个主要是利用迭代器来判断是否是蔬菜,这边用了 try catch 语句,如果出错也就是如果是菜单的话就会捕获异常,并跳过try的语句,相当于 continue语句.

测试类

package composite;

public class MenuTestDriver {
    public static void main(String[] args) {
        //早餐菜单
        MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU", "Breakfast");
        //午餐菜单
        MenuComponent dinnerMenu = new Menu("DINNER MENU", "Lunch");
        //晚餐菜单
        MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner");
        //晚餐甜点菜单
        MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course!");
        //主菜单
        MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");
        
        //菜单加入元素
        
        allMenus.add(pancakeHouseMenu);
        allMenus.add(dinnerMenu);
        allMenus.add(cafeMenu);
        
        dinnerMenu.add(new MenuItem("Pasta", "a slice of sourdough bread", true, 3.89));
        dinnerMenu.add(dessertMenu);
        
        dessertMenu.add(new MenuItem("Apple Pie", "Apple Pie", false, 3.21));
        
        Waitress waitress = new Waitress(allMenus);
        waitress.printMenu();
        System.out.println("------------------素食----------------");
        waitress.printVegetarianMenu();
    }
}

结果

 
 ALL MENUS , All menus combined
 ---------------- 
 
 PANCAKE HOUSE MENU , Breakfast
 ---------------- 
 
 DINNER MENU , Lunch
 ---------------- 
 Pasta(V) , 3.89
  --a slice of sourdough bread
 
 DESSERT MENU , Dessert of course!
 ---------------- 
 Apple Pie , 3.21
  --Apple Pie
 
 CAFE MENU , Dinner
 ---------------- 
------------------素食----------------
-------------VEGETRAIAN MENU--------------
 Pasta(V) , 3.89
  --a slice of sourdough bread

3.总结

组合就是把树形结构的每个元素都当成一个类型,不管是 组合 还是叶节点都是相同对待,这就是透明性,对于客户来说菜单和菜单项都是一样的,通过调用相同的方法就能实现一样的功能.

思想很好理解,树形结构,大家都是一种类型.

原文地址:https://www.cnblogs.com/lishuaiqi/p/11295617.html