Java设计模式——迭代器模式和组合模式

Java设计模式之迭代器模式以及组合模式

迭代器模式

迭代器模式:提供一种方法顺序访问一个聚合对象的各个元素,而又不暴露其内部的表示。

  • 用于封装对象的遍历,无论是数组存储还是ArrayList,调用统一的接口,根据多态调用。
  • 迭代器接口:定义迭代器的方法
  • 具体迭代器:实现不同对象的迭代器方法的不同实现

迭代器模式让我们游走在聚合内的每一个元素,而又不暴露内部的实现。

Head first 设计模式例子

有一个饭店有多个菜单,由女招待把菜单的内容展示出来,这里使用迭代器模式,每个菜单都是由其他公司合并的,有不同的实现方式(数组、列表、链表)

  • Menu
public interface Menu {
    public void addItem(String name,String price,String des);
    public Iterator createIterator();
}
  • MenuItem
@Data
public class MenuItem {
    private String name;
    private String des;
    private String price;

    public MenuItem(String name, String des, String price) {
        this.name = name;
        this.des = des;
        this.price = price;
    }
}
  • Iterator
public interface Iterator {
    public Object next();
    public boolean hasNext();
}
  • 列表式菜单——CakeMenu
public class CakeMenu implements Menu {
    private ArrayList<MenuItem> cakeItems;
    public CakeMenu(){
        cakeItems = new ArrayList<>();
        addItem("Bread","10.00","面包");
        addItem("Ice-cream","15.00","冰淇淋");
        addItem("Cake","20.00","饼干");
    }
    public void addItem(String name,String price,String des) {
        MenuItem item = new MenuItem(name,price,des);
        cakeItems.add(item);
    }

    public Iterator createIterator() {
        return new ListIterator(cakeItems);
    }
}
  • 数组式菜单——CoffeeMenu
public class CoffeeMenu implements Menu {
    private MenuItem[] menuItems;
    private final int MAX_SIZE = 10;
    private int i;
    public CoffeeMenu(){
        menuItems = new MenuItem[MAX_SIZE];
        addItem("Moka","10.00","摩卡咖啡");
        addItem("Fragrant","15.00","香浓咖啡");
        addItem("Milk","20.00","牛奶咖啡");
    }
    public void addItem(String name,String price,String des) {
        MenuItem item = new MenuItem(name,price,des);
        menuItems[i] = item;
        i++;
    }

    public Iterator createIterator() {
        return new ArrayIterator(menuItems);
    }
}
  • 链表式菜单——DinnerMenu
public class DinnerMenu implements Menu {
    private LinkedList<MenuItem> dinnerItems;
    private int i;
    public DinnerMenu(){
        dinnerItems = new LinkedList<>();
        addItem("fish","100.00","糖醋鱼");
        addItem("chicken","125.00","辣子鸡");
        addItem("duck","200.00","北京烤鸭");
    }
    public void addItem(String name,String price,String des) {
        MenuItem item = new MenuItem(name,price,des);
        dinnerItems.add(item);
    }

    public Iterator createIterator() {
        return new ListIterator(dinnerItems);
    }
}
  • ArrayIterator
public class ArrayIterator implements Iterator {
    private MenuItem[] menuItems;
    private int index;
    public ArrayIterator(MenuItem[] menuItems){
        this.menuItems = menuItems;
    }
    @Override
    public Object next() {
        return menuItems[index++];
    }

    @Override
    public boolean hasNext() {
        if(index==menuItems.length||menuItems[index]==null){
            return false;
        }
        return true;
    }
}
  • ListIterator
public class ListIterator implements Iterator {
    private List<MenuItem> menuItems;
    private int index = 0;
    public ListIterator(List<MenuItem> menuItems){
        this.menuItems = menuItems;
    }
    @Override
    public Object next() {
        return menuItems.get(index++);
    }

    @Override
    public boolean hasNext() {
        if(index == menuItems.size()||menuItems.get(index)==null){
            return false;
        }
        return true;
    }
}
  • Waitress
public class Waitress {
    private Menu cakeMenu;
    private Menu coffeeMenu;
    private Menu dinnerMenu;
    public Waitress(Menu cakeMenu,Menu coffeeMenu,Menu dinnerMenu){
        this.cakeMenu = cakeMenu;
        this.coffeeMenu = coffeeMenu;
        this.dinnerMenu = dinnerMenu;
    }
    public void printMenu(){
        Iterator cakeI = cakeMenu.createIterator();
        Iterator coffeeI = coffeeMenu.createIterator();
        Iterator dinnerI = dinnerMenu.createIterator();
        System.out.println("Menu is here:");
        System.out.println("Cake:");
        printMenu(cakeI);
        System.out.println("Coffee:");
        printMenu(coffeeI);
        System.out.println("Dinner:");
        printMenu(dinnerI);
    }

    private void printMenu(Iterator iterator) {
        while (iterator.hasNext()){
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.println(menuItem);
        }
    }
}
  • 测试
public static void main(String[] args) {
        CakeMenu cakeMenu = new CakeMenu();
        CoffeeMenu coffeeMenu = new CoffeeMenu();
        DinnerMenu dinnerMenu = new DinnerMenu();
        Waitress waitress = new Waitress(cakeMenu,coffeeMenu,dinnerMenu);
        waitress.printMenu();
    }

——output
Menu is here:
Cake:
MenuItem{name='Bread', des='10.00', price='面包'}
MenuItem{name='Ice-cream', des='15.00', price='冰淇淋'}
MenuItem{name='Cake', des='20.00', price='饼干'}
Coffee:
MenuItem{name='Moka', des='10.00', price='摩卡咖啡'}
MenuItem{name='Fragrant', des='15.00', price='香浓咖啡'}
MenuItem{name='Milk', des='20.00', price='牛奶咖啡'}
Dinner:
MenuItem{name='fish', des='100.00', price='糖醋鱼'}
MenuItem{name='chicken', des='125.00', price='辣子鸡'}
MenuItem{name='duck', des='200.00', price='北京烤鸭'}

单一原则

新的设计原则:

  • 单一原则:一个类应该只有一个引起变化的原因。

内聚:度量一个类或者模块的紧密程度。

  • 当一类或模块被设计成支持一组不相关的功能时,它具有低内聚
  • 被设计成支持一组相关的功能时,具有高内聚

组合模式

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

以上面这个例子来说就是:我们的菜单中出现新的小菜单时,让我们处理MenuItem的方式和处理小菜单的方式是一样的,客户不需要知道它具体处理的是什么,因为它会根据多态进行处理。换句话说:我们可以忽略组合和个别对象的差别。

  • 所以他们必须有相同的基类型

组合模式让我们用树型的方式创建对象的结构,树中包含了组合个别的对象

菜单需求:大菜单中有小菜单(对象组合)和食物(个别对象),形成树型结构

修改后

  • MenuComponent

    这里把菜单组件类定义为抽象类是因为ItemMenu没必要全部实现这些方法,各自实现需要用到的就好了,没有用到的被调用的话使用该抽象类提供的默认实现:抛出不支持异常。(Java中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 getDes(){
        throw new UnsupportedOperationException();
    }
    public String getPrice(){
        throw new UnsupportedOperationException();
    }
    public void print(){
        throw new UnsupportedOperationException();
    }
}
  • Menu(对象组合)
public class Menu extends MenuComponent {
    private ArrayList<MenuComponent> menuList;
    private String name;
    private String des;
    public Menu(String name,String des){
        menuList = new ArrayList<>();
        this.des = des;
        this.name = name;
    }
    @Override
    public void print() {
        System.out.println("menu name:"+getName()+
                "\n"+"menu des:"+getDes());
        Iterator iterator = menuList.iterator();
        while (iterator.hasNext()){
            MenuComponent component = (MenuComponent) iterator.next();
            component.print();
        }
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public String getDes() {
        return des;
    }
    @Override
    public MenuComponent getChild(int i) {
        return menuList.get(i);
    }
    @Override
    public void add(MenuComponent menuComponent) {
        menuList.add(menuComponent);
    }
    @Override
    public void remove(MenuComponent menuComponent) {
        menuList.remove(menuComponent);
    }
}
  • MenuItem(个别对象)
public class MenuItem extends MenuComponent {
    private String name;
    private String des;
    private String price;
    public MenuItem(String name, String des, String price) {
        this.name = name;
        this.des = des;
        this.price = price;
    }
    @Override
    public String getDes() {
        return des;
    }
    @Override
    public String getPrice() {
        return price;
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public void print() {
        System.out.println("MenuItem{" +
                "name='" + name + '\'' +
                ", des='" + des + '\'' +
                ", price='" + price + '\'' +
                '}');
    }
}
  • Waitress
public class Waitress {
    private MenuComponent allMenus;
    public Waitress(MenuComponent allMenus){
        this.allMenus = allMenus;
    }
    public void printMenu(){
        allMenus.print();
    }
}
  • 测试
 public static void main(String[] args) {
        MenuComponent cakeMenu = new Menu("cake","饼干类");
        MenuComponent dinnerMenu = new Menu("dinner","晚餐");
        MenuComponent coffeeMenu = new Menu("coffee","小吃甜点咖啡");
        cakeMenu.add(new MenuItem("Bread","10.00","面包"));
        cakeMenu.add(new MenuItem("Ice-cream","15.00","冰淇淋"));
        cakeMenu.add(new MenuItem("Cake","20.00","饼干"));

        coffeeMenu.add(new MenuItem("Moka","10.00","摩卡咖啡"));
        coffeeMenu.add(new MenuItem("Fragrant","15.00","香浓咖啡"));
        coffeeMenu.add(new MenuItem("Milk","20.00","牛奶咖啡"));

        dinnerMenu.add(new MenuItem("fish","100.00","糖醋鱼"));
        dinnerMenu.add(new MenuItem("chicken","125.00","辣子鸡"));
        dinnerMenu.add(new MenuItem("duck","200.00","北京烤鸭"));

        MenuComponent allMenus = new Menu("allMenus","所有菜单");
        allMenus.add(cakeMenu);
        allMenus.add(dinnerMenu);
        allMenus.add(coffeeMenu);
        Waitress waitress = new Waitress(allMenus);
        waitress.printMenu();
    }
  • output
menu name:allMenus
menu des:所有菜单
menu name:cake
menu des:饼干类
MenuItem{name='Bread', des='10.00', price='面包'}
MenuItem{name='Ice-cream', des='15.00', price='冰淇淋'}
MenuItem{name='Cake', des='20.00', price='饼干'}
menu name:dinner
menu des:晚餐
MenuItem{name='fish', des='100.00', price='糖醋鱼'}
MenuItem{name='chicken', des='125.00', price='辣子鸡'}
MenuItem{name='duck', des='200.00', price='北京烤鸭'}
menu name:coffee
menu des:小吃甜点咖啡
MenuItem{name='Moka', des='10.00', price='摩卡咖啡'}
MenuItem{name='Fragrant', des='15.00', price='香浓咖啡'}
MenuItem{name='Milk', des='20.00', price='牛奶咖啡'}

但是这么一看,好像违反了单一原则,一个类不但要管理Item还要管理Menu

  • 但是它这样做得到的是对象和对象组合的一致,被一视同仁对待。如果加了判断是否为Item和Menu的代码就失去了在这种性质

总结

迭代器模式:

  • 迭代器允许访问聚合的元素,而不需要暴露它内部实现。
  • 迭代器将遍历工作封装进对象中,解决不同的遍历实现。
  • 迭代器提供一个通用接口遍历,根据多态机制。
  • 努力使用一个类一个责任原则

组合模式:

  • 提供一个树型结构存储对象和对象组合
  • 组合模式将单个对象和对象组合一视同仁
  • 任意对象或组合都是一个组件(抽象基类继承)
  • 透明性:客户将组合和对象一视同仁

总结一下学到的设计原则:

  • 类应该对扩展开放,对修改关闭

  • 把变化的抽取出来,不要和不变化的放在一起。

  • 针对接口编程,不针对实现编程。

  • 多用组合,少用继承。

  • 单一原则:一个类应该只有一个引起变化的原因。

  • 依赖倒置:要依赖抽象,不依赖具体类。

  • 为了交互对象之间的松耦合的设计而努力。

  • 好莱坞原则:别调用我们,我们会调用你。

  • 最少设计原则:只和你的密友谈话(减少耦合)。

代码地址:Github迭代器模式 Github组合模式


Java设计模式|策略模式

Java设计模式|观察者模式

Java设计模式|装饰者模式

Java设计模式|工厂模式

Java设计模式|命令模式

Java设计模式|适配器模式和外观模式

Java设计模式|模板方法模式

Java设计模式|迭代器模式和组合模式

Java设计模式|状态模式

Java设计模式|代理模式

Java设计模式|单例模式

Java设计模式|备忘录模式

Java设计模式|访问者模式

Java设计模式|复合模式

Java设计模式|桥接模式

Java设计模式|生成器模式

Java设计模式|享元模式/蝇量模式

Java设计模式|原型模式

Java设计模式|责任链模式

Java设计模式|中介者模式

  • 本文作者: dzou | 微信:17856530567
  • 本文链接: http://www.dzou.top/post/7244f815.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
  • 并保留本声明和上方二维码。感谢您的阅读和支持!