设计模式——模板、命令、访问者、迭代器

80 阅读16分钟

模板方法模式

实现豆浆制作问题

编写制作豆浆的程序,说明如下:

  • 制作豆浆的流程 选材--->添加配料--->浸泡--->放到豆浆机打碎
  • 通过添加不同的配料,可以制作出不同口味的豆浆
  • 选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆 浆都是一样的(红豆、花生豆浆。。。)

基本介绍

  1. 模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
  2. 简单说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类不可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
  3. 这种类型的设计模式属于行为型模式

原理类图

mb1.png

  • AbstractClass 抽象类,类中实现了模板方法,具体子类需要去实现其他的抽象方法operationr2,3,4 mb2.png 豆浆抽象类
package designmode.template;

//抽象类,表示豆浆
public abstract class SoyaMilk {

    //模板方法, make , 模板方法可以做成final , 不让子类去覆盖.
    final void make() {
        select();
        if(customerWantCondiments()) {
            addCondiments();
        }
        soak();
        beat();
    }

    //选材料
    void select() {
        System.out.println("第一步:选择好的新鲜黄豆  ");
    }

    //添加不同的配料, 抽象方法, 子类具体实现
    abstract void addCondiments();

    //浸泡
    void soak() {
        System.out.println("第三步, 黄豆和配料开始浸泡, 需要3小时 ");
    }

    void beat() {
        System.out.println("第四步:黄豆和配料放到豆浆机去打碎  ");
    }

    //钩子方法,决定是否需要添加配料
    boolean customerWantCondiments() {
        return true;
    }
}

花生豆浆

package designmode.template;

public class PeanutSoyaMilk extends SoyaMilk {

    @Override
    void addCondiments() {
        // TODO Auto-generated method stub
        System.out.println(" 加入上好的花生 ");
    }
}

红豆豆浆

package designmode.template;

public class RedBeanSoyaMilk extends SoyaMilk{
    @Override
    void addCondiments() {
        System.out.println(" 加入上好的红豆 ");
    }
}

纯豆浆(空实现)

package designmode.template;

public class PureSoyaMilk extends SoyaMilk{

    @Override
    void addCondiments() {
        //空实现
    }

    @Override
    boolean customerWantCondiments() {
        return false;
    }

}

客户端打印

package designmode.template;
public class Client {
    public static void main(String[] args) {
        //制作红豆豆浆
        System.out.println("----制作红豆豆浆----");
        SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
        redBeanSoyaMilk.make();

        System.out.println("----制作花生豆浆----");
        SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
        peanutSoyaMilk.make();

        System.out.println("----制作纯豆浆----");
        SoyaMilk pureSoyaMilk = new PureSoyaMilk();
        pureSoyaMilk.make();
    }
}

mb5.png

模板方法模式的钩子方法

  • 在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为"钩子";
  • 还是用上面做豆浆的例子来讲解,比如,我们还希望制作纯豆浆,不添加任何的配料,请使用钩子方法对前面的模板方法进行改造

模板方法模式在Spring框架应用的源码分析

  • Spring IOC容器初始化时运用到的模板方法模式
  • 代码分析 + 角色分析 + 说明类图

mb3.png

  • 源码层次说明

mb4.png

 模板方法模式的注意事项和细节

  1. 基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
  2. 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
  3. 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
  4. 该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
  5. 一般模板方法都加上final关键字, 防止子类重写模板方法.
  6. 模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理

命令模式

看一个具体需求

  1. 我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装app就可以控制对这些家电工作。
  2. 这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个App,分别控制,我们希望只要一个app就可以控制全部智能家电。
  3. 要实现一个app控制所有智能家电的需要,则每个智能家电厂家都要提供一个统一的接口给app调用,这时 就可以考虑使用命令模式。
  4. 命令模式可将“动作的请求者”从“动作的执行者”对象中解耦出来.
  5. 在我们的例子中,动作的请求者是手机app,动作的执行者是每个厂商的一个家电产品

命令模式基本介绍

  1. 命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
  2. 命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
  3. 在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
  4. 通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。Invoker是调用者(将军),Receiver是被调用者(士兵),MyComman是命令,实现了Command接口,持有接收对象

命令模式的原理类图

ml1.png 职责说明

  1. Invoker: 是调用者角色
  2. Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
  3. Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作
  4. ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute

命令模式解决智能生活项目

思路分析和图解

ml2.png 代码实现

package designmode.command;

//创建命令接口
public interface Command {

    //执行动作(操作)
    public void execute();
    //撤销动作(操作)
    public void undo();
}

开灯的命名实现LightOnCommand

package designmode.command;

public class LightOnCommand implements Command {

    //聚合LightReceiver
    LightReceiver light;

    //构造器
    public LightOnCommand(LightReceiver light) {
        super();
        this.light = light;
    }

    @Override
    public void execute() {
        // TODO Auto-generated method stub
        //调用接收者的方法
        light.on();
    }



    @Override
    public void undo() {
        // TODO Auto-generated method stub
        //调用接收者的方法
        light.off();
    }

}

关灯的命令实现LightOffCommand

package designmode.command;

public class LightOffCommand implements Command {

    // 聚合LightReceiver
    LightReceiver light;

    // 构造器
    public LightOffCommand(LightReceiver light) {
        super();
        this.light = light;
    }

    @Override
    public void execute() {
        // 调用接收者的方法
        light.off();
    }

    @Override
    public void undo() {
        // 调用接收者的方法
        light.on();
    }
}

没有任何命令,即空执行

package designmode.command;

/**
 * 没有任何命令,即空执行: 用于初始化每个按钮, 当调用空命令时,对象什么都不做
 * 其实,这样是一种设计模式, 可以省掉对空判断
 * @author Administrator
 *
 */
public class NoCommand implements Command {

    @Override
    public void execute() {
        // TODO Auto-generated method stub

    }

    @Override
    public void undo() {
        // TODO Auto-generated method stub

    }

}

被命令的对象LightReceiver

package designmode.command;

public class LightReceiver {

    public void on() {
        System.out.println(" 电灯打开了.. ");
    }

    public void off() {
        System.out.println(" 电灯关闭了.. ");
    }
}

TV开命令实现TVOnCommand

package designmode.command;

public class TVOnCommand implements Command {

    // 聚合TVReceiver
    TVReceiver tv;

    // 构造器
    public TVOnCommand(TVReceiver tv) {
        super();
        this.tv = tv;
    }

    @Override
    public void execute() {
        // 调用接收者的方法
        tv.on();
    }

    @Override
    public void undo() {
        // 调用接收者的方法
        tv.off();
    }
}

TV关命令实现TVOffCommand

package designmode.command;

public class TVOffCommand implements Command {

    // 聚合TVReceiver
    TVReceiver tv;

    // 构造器
    public TVOffCommand(TVReceiver tv) {
        super();
        this.tv = tv;
    }

    @Override
    public void execute() {
        // TODO Auto-generated method stub
        // 调用接收者的方法
        tv.off();
    }

    @Override
    public void undo() {
        // TODO Auto-generated method stub
        // 调用接收者的方法
        tv.on();
    }
}

被命令对象TV

package designmode.command;

public class TVReceiver {

    public void on() {
        System.out.println(" 电视机打开了.. ");
    }

    public void off() {
        System.out.println(" 电视机关闭了.. ");
    }
}

遥控器,执行命名RemoteController

package designmode.command;

public class RemoteController {

    // 开 按钮的命令数组
    Command[] onCommands;
    Command[] offCommands;

    // 执行撤销的命令
    Command undoCommand;

    // 构造器,完成对按钮初始化

    public RemoteController() {
        onCommands = new Command[5];
        offCommands = new Command[5];

        for (int i = 0; i < 5; i++) {
            onCommands[i] = new NoCommand();
            offCommands[i] = new NoCommand();
        }
    }

    // 给我们的按钮设置你需要的命令
    public void setCommand(int no, Command onCommand, Command offCommand) {
        onCommands[no] = onCommand;
        offCommands[no] = offCommand;
    }

    // 按下开按钮
    public void onButtonWasPushed(int no) { // no 0
        // 找到你按下的开的按钮, 并调用对应方法
        onCommands[no].execute();
        // 记录这次的操作,用于撤销
        undoCommand = onCommands[no];
    }

    // 按下开按钮
    public void offButtonWasPushed(int no) { // no 0
        // 找到你按下的关的按钮, 并调用对应方法
        offCommands[no].execute();
        // 记录这次的操作,用于撤销
        undoCommand = offCommands[no];
    }

    // 按下撤销按钮
    public void undoButtonWasPushed() {
        undoCommand.undo();
    }

}

客户端打印输出

package designmode.command;

public class Client {

    public static void main(String[] args) {
        //使用命令设计模式,完成通过遥控器,对电灯的操作

        //创建电灯的对象(接受者)
        LightReceiver lightReceiver = new LightReceiver();

        //创建电灯相关的开关命令
        LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
        LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);

        //需要一个遥控器
        RemoteController remoteController = new RemoteController();

        //给我们的遥控器设置命令, 比如 no = 0 是电灯的开和关的操作
        remoteController.setCommand(0, lightOnCommand, lightOffCommand);

        System.out.println("--------按下灯的开按钮-----------");
        remoteController.onButtonWasPushed(0);
        System.out.println("--------按下灯的关按钮-----------");
        remoteController.offButtonWasPushed(0);
        System.out.println("--------按下撤销按钮-----------");
        remoteController.undoButtonWasPushed();


        System.out.println("=========使用遥控器操作电视机==========");

        TVReceiver tvReceiver = new TVReceiver();

        TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver);
        TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver);

        //给我们的遥控器设置命令, 比如 no = 1 是电视机的开和关的操作
        remoteController.setCommand(1, tvOnCommand, tvOffCommand);

        System.out.println("--------按下电视机的开按钮-----------");
        remoteController.onButtonWasPushed(1);
        System.out.println("--------按下电视机的关按钮-----------");
        remoteController.offButtonWasPushed(1);
        System.out.println("--------按下撤销按钮-----------");
        remoteController.undoButtonWasPushed();

    }

}

fw4.png

命令模式在Spring框架JdbcTemplate应用的源码分析

  1. Spring 框架的JdbcTemplate就使用到了命令模式
  2. 代码分析

ml3.png 3) 角色分析说明

  • StatementCallback 接口 ,类似命令接口(Command)
  • class QueryStatementCallback implements StatementCallback<T>, SqlProvider , 匿名内部类, 实现了命令接口, 同时也充当命令接收者
  • 命令调用者 是 JdbcTemplate , 其中execute(StatementCallback<T> action) 方法中,调用action.doInStatement 方法. 不同的 实现 StatementCallback接口的对象,对应不同的doInStatemnt实现逻辑
  • 另外实现 StatementCallback 命令接口的子类还有 QueryStatementCallback

ml4.png

命令模式的注意事项和细节

  1. 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
  2. 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
  3. 容易实现对请求的撤销和重做
  4. 命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意
  5. 空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
  6. 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟CMD(DOS命令)订单的撤销/恢复、触发-反馈机制

访问者模式

测评系统的需求

完成测评系统需求

  1. 将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价 有不同的种类,比如 成功、失败 等)

fw1.png

传统方式的问题分析

  1. 如果系统比较小,还是ok的,但是考虑系统增加越来越多新的功能时,对代码改动较大,违反了ocp原则, 不利于维护
  2. 扩展性不好,比如 增加了 新的人员类型,或者管理方法,都不好做
  3. 引出我们会使用新的设计模式 – 访问者模式

访问者模式基本介绍

  1. 访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
  2. 主要将数据结构与数据操作分离,解决 数据结构和操作耦合性问题
  3. 访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口
  4. 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决

访问者模式的原理类图

fw2.png 对原理类图的说明--即(访问者模式的角色及职责)

  1. Visitor 是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作
  2. ConcreteVisitor :是一个具体的访问值 实现每个有Visitor 声明的操作,是每个操作实现的部分.
  3. ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
  4. Element 定义一个accept 方法,接收一个访问者对象
  5. ConcreteElement 为具体元素,实现了accept 方法

访问者模式应用实例

将人分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得 到他们对该歌手不同的评价(评价有不同的种类,比如成功、失败等), 请使用访问者模式来说实现 fw3.png Action

package designmode.visitor;

public abstract class Action {

    //得到男性 的测评
    public abstract void getManResult(Man man);

    //得到女的 测评
    public abstract void getWomanResult(Woman woman);
}

失败评分Fail

package designmode.visitor;

public class Fail extends Action {

    @Override
    public void getManResult(Man man) {
        // TODO Auto-generated method stub
        System.out.println(" 男人给的评价该歌手失败 !");
    }

    @Override
    public void getWomanResult(Woman woman) {
        // TODO Auto-generated method stub
        System.out.println(" 女人给的评价该歌手失败 !");
    }
}

成功评分Success

package designmode.visitor;

public class Success extends Action {

    @Override
    public void getManResult(Man man) {
        System.out.println(" 男人给的评价该歌手很成功 !");
    }

    @Override
    public void getWomanResult(Woman woman) {
        System.out.println(" 女人给的评价该歌手很成功 !");
    }
}

男女性的评分接口

package designmode.visitor;

public abstract class Person {

    //提供一个方法,让访问者可以访问
    public abstract void accept(Action action);
}

Woman

public class Woman extends Person{

    @Override
    public void accept(Action action) {
        // TODO Auto-generated method stub
        action.getWomanResult(this);
    }

}

Man

package designmode.visitor;

public class Man extends Person {

    @Override
    public void accept(Action action) {
        // TODO Auto-generated method stub
        action.getManResult(this);
    }

}
  1. 这里我们使用到了双分派, 即首先在客户端程序中,将具体状态作为参数传递Woman中(第一次分派)
  2. 然后Woman 类调用作为参数的 "具体方法" 中方法getWomanResult, 同时将自己(this)作为参数 传入,完成第二次的分派 数据结构,管理很多人(Man , Woman)
package designmode.visitor;

import java.util.LinkedList;
import java.util.List;

public class ObjectStructure {

    //维护了一个集合
    private List<Person> persons = new LinkedList<>();

    //增加到list
    public void attach(Person p) {
        persons.add(p);
    }
    //移除
    public void detach(Person p) {
        persons.remove(p);
    }

    //显示测评情况
    public void display(Action action) {
        for(Person p: persons) {
            p.accept(action);
        }
    }
}

这时候我们如果再多一种评分待定,只需要建个待定类即可

package designmode.visitor;

public class Wait extends Action {

    @Override
    public void getManResult(Man man) {
        // TODO Auto-generated method stub
        System.out.println(" 男人给的评价是该歌手待定 ..");
    }

    @Override
    public void getWomanResult(Woman woman) {
        // TODO Auto-generated method stub
        System.out.println(" 女人给的评价是该歌手待定 ..");
    }

}

Client

package designmode.visitor;

public class Client {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //创建ObjectStructure
        ObjectStructure objectStructure = new ObjectStructure();

        objectStructure.attach(new Man());
        objectStructure.attach(new Woman());
        
        //成功
        Success success = new Success();
        objectStructure.display(success);

        System.out.println("===============");
        Fail fail = new Fail();
        objectStructure.display(fail);

        System.out.println("=======给的是待定的测评========");

        Wait wait = new Wait();
        objectStructure.display(wait);
    }
}

fw4.png

应用案例的小结-双分派

  • 上面提到了双分派,所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行。双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型
  • 以上述实例为例,假设我们要添加一个Wait的状态类,考察Man类和Woman类的反应,由于使用了双分派,只需增加一个Action子类即可在客户端调用即可,不需要改动任何其他类的代码

访问者模式的注意事项和细节

  • 优点
  1. 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
  2. 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
  • 缺点
  1. 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难
  2. 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
  3. 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.

迭代器模式

应用需求

编写程序展示一个学校院系结构:需求是这样,要在一个页面中 展示出学校的院系组成,一个学校有多个学院,一个学院有多 个系。如图:

dd1.png

传统的设计方案(类图)

dd2.png

传统的方式的问题分析

  1. 将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的
  2. 实际上我们的要求是 :在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系, 因此这种方案,不能很好实现的遍历的操作
  3. 解决方案:=> 迭代器模式

迭代器模式基本介绍

  1. 迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式
  2. 如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
  3. 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。

迭代器模式的原理类图

dd3.png

  1. Iterator:迭代器接口,是系统提供,含义 hasnext,next, remove
  2. Concretelterator:具体的迭代器类,管理迭代
  3. Aggregate:一个统一的聚合接口,将客户端和具体聚合解耦
  4. Concreteaggreage:具体的聚合持有对象集合,并提供一个方法,返回一个迭代器,该迭代器可以正确遍历集合
  5. Client:客户端,通过 IteratorAggregate依赖子类

迭代器模式应用实例

编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。

dd4.png

代码实现

Department

package designmode.iterator;

//系
public class Department {

    private String name;
    private String desc;
    public Department(String name, String desc) {
        super();
        this.name = name;
        this.desc = desc;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDesc() {
        return desc;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }
}

计算机系迭代类ComputerCollegeIterator

package designmode.iterator;

import java.util.Iterator;

public class ComputerCollegeIterator implements Iterator {

    //这里我们需要Department 是以怎样的方式存放=>数组
    Department[] departments;
    int position = 0; //遍历的位置

    public ComputerCollegeIterator(Department[] departments) {
        this.departments = departments;
    }

    //判断是否还有下一个元素
    @Override
    public boolean hasNext() {
        // TODO Auto-generated method stub
        if(position >= departments.length || departments[position] == null) {
            return false;
        }else {
            return true;
        }
    }

    @Override
    public Object next() {
        // TODO Auto-generated method stub
        Department department = departments[position];
        position += 1;
        return department;
    }

    //删除的方法,默认空实现
    public void remove() {}
}

信息系迭代器InfoColleageIterator

package designmode.iterator;

import java.util.Iterator;
import java.util.List;

public class InfoColleageIterator implements Iterator {

    List<Department> departmentList; // 信息工程学院是以List方式存放系
    int index = -1;//索引

    public InfoColleageIterator(List<Department> departmentList) {
        this.departmentList = departmentList;
    }

    //判断list中还有没有下一个元素
    @Override
    public boolean hasNext() {
        // TODO Auto-generated method stub
        if(index >= departmentList.size() - 1) {
            return false;
        } else {
            index += 1;
            return true;
        }
    }

    @Override
    public Object next() {
        // TODO Auto-generated method stub
        return departmentList.get(index);
    }

    //空实现remove
    public void remove() { }
}

学院接口College

package designmode.iterator;

import java.util.Iterator;

public interface College {

    public String getName();

    //增加系的方法
    public void addDepartment(String name, String desc);

    //返回一个迭代器,遍历
    public Iterator createIterator();
}

计算机学院ComputerCollege

package designmode.iterator;

import java.util.Iterator;

public class ComputerCollege implements College {

    Department[] departments;
    int numOfDepartment = 0 ;// 保存当前数组的对象个数
    
    public ComputerCollege() {
        departments = new Department[5];
        addDepartment("Java专业", " Java专业 ");
        addDepartment("PHP专业", " PHP专业 ");
        addDepartment("大数据专业", " 大数据专业 ");
    }
    
    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return "计算机学院";
    }

    @Override
    public void addDepartment(String name, String desc) {
        // TODO Auto-generated method stub
        Department department = new Department(name, desc);
        departments[numOfDepartment] = department;
        numOfDepartment += 1;
    }

    @Override
    public Iterator createIterator() {
        // TODO Auto-generated method stub
        return new ComputerCollegeIterator(departments);
    }

}

信息学院InfoCollege

package designmode.iterator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class InfoCollege implements College {

    List<Department> departmentList;
    
    public InfoCollege() {
        departmentList = new ArrayList<Department>();
        addDepartment("信息安全专业", " 信息安全专业 ");
        addDepartment("网络安全专业", " 网络安全专业 ");
        addDepartment("服务器安全专业", " 服务器安全专业 ");
    }

    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return "信息工程学院";
    }

    @Override
    public void addDepartment(String name, String desc) {
        // TODO Auto-generated method stub
        Department department = new Department(name, desc);
        departmentList.add(department);
    }

    @Override
    public Iterator createIterator() {
        // TODO Auto-generated method stub
        return new InfoColleageIterator(departmentList);
    }
}

打印输出类OutPutImpl

package designmode.iterator;

import java.util.Iterator;
import java.util.List;

public class OutPutImpl {

    //学院集合
    List<College> collegeList;

    public OutPutImpl(List<College> collegeList) {
        this.collegeList = collegeList;
    }
    //遍历所有学院,然后调用printDepartment 输出各个学院的系
    public void printCollege() {

        //从collegeList 取出所有学院, Java 中的 List 已经实现Iterator
        Iterator<College> iterator = collegeList.iterator();
        while(iterator.hasNext()) {
            //取出一个学院
            College college = iterator.next();
            System.out.println("=== "+college.getName() +"=====" );
            printDepartment(college.createIterator()); //得到对应迭代器
        }
    }


    //输出 学院输出 系
    public void printDepartment(Iterator iterator) {
        while(iterator.hasNext()) {
            Department d = (Department)iterator.next();
            System.out.println(d.getName());
        }
    }
}

Client

package designmode.iterator;

import java.util.ArrayList;
import java.util.List;

public class Client {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //创建学院
        List<College> collegeList = new ArrayList<College>();

        ComputerCollege computerCollege = new ComputerCollege();
        InfoCollege infoCollege = new InfoCollege();

        collegeList.add(computerCollege);
        //collegeList.add(infoCollege);

        OutPutImpl outPutImpl = new OutPutImpl(collegeList);
        outPutImpl.printCollege();
    }

}

dd5.png

迭代器模式在JDK-ArrayList集合应用的源码分析

1) JDKArrayList集合中就使用了迭代器模式。 2) 代码分析+类图+说明 dd6.png

dd7.png 3) 对类图的角色分析和说明

  • 内部类Itr 充当具体实现迭代器Iterator 的类, 作为ArrayList 内部类
  • List 就是充当了聚合接口,含有一个iterator() 方法,返回一个迭代器对象
  • ArrayList 是实现聚合接口List 的子类,实现了iterator()
  • Iterator 接口系统提供
  • 迭代器模式解决了 不同集合(ArrayList ,LinkedList) 统一遍历问题

迭代器模式的注意事项和细节

  • 优点
  1. 提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
  2. 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
  3. 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
  4. 当要展示一组相似对象,或者遍历一组相同对象时使用,适合使用迭代器模式
  • 缺点 每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类