zoukankan      html  css  js  c++  java
  • 组合模式

    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.总结

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

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

  • 相关阅读:
    Codeforces 627A XOR Equation(思路)
    Codeforces 633C Spy Syndrome 2(DP + Trie树)
    BZOJ 1982 [Spoj 2021]Moving Pebbles(博弈论)
    BZOJ 3676 [Apio2014]回文串(回文树)
    BZOJ 3790 神奇项链(manacher+DP+树状数组)
    Codeforces 449D Jzzhu and Numbers(高维前缀和)
    SPOJ Time Limit Exceeded(高维前缀和)
    BZOJ 4031 [HEOI2015]小Z的房间(Matrix-Tree定理)
    BZOJ 3809 Gty的二逼妹子序列(莫队+分块)
    BZOJ 3544 [ONTAK2010]Creative Accounting(set)
  • 原文地址:https://www.cnblogs.com/lishuaiqi/p/11295617.html
Copyright © 2011-2022 走看看