Day 21: Behavioral patterns - Iterator

目的

了解 forEach() 的来由。

说明

简单来说,实作任意资料结构的 forEach(),最常见的莫过於 ArrayList 等,其他像是 Linked ListStackQueueTree 等也是可实作的资料结构,在模式内负责储存的物件称作 Aggregate

这次的实作,采用 Array(JS)、 List(Java),放入名为 Car 的物件。

相关作法是:

  1. 建立 Aggregate 的虚拟层亲代,提供基本的 Array(JS)、 List(Java) 方法,像是:
    1. 取得 Array(JS)、 List(Java) 长度。
    2. 取得特定项目。
    3. 新增项目。
    4. 与 Iterator 连结。
  2. 建立 Iterator 的虚拟层亲代,负责与 Aggregate 沟通,取得 Aggregate 内的项目。
  3. 实作 Aggregate 的子代,除了亲代的方法之外,还要内建一个 Array(JS)、 List(Java) 负责储存物件。
  4. 实作 Iterator 的子代,除了亲代的方法之外,还可以依照需求建立新方法,与 Aggregate 内的项目互动。
  5. 输入多个 Car 物件後,搭配 While 模拟 forEach()

以下范例以「依序检查车库内的二手车辆」为核心制作。

UML 图

Iterator Pattern UML Diagram

使用 Java 实作

放入 Aggregate 内的物件:Car


    private String name;
    private String manufacturer;
    private int productionYear;
    private int gasolinePercent;

    public Car(String name, String manufacturer, int productionYear, int gasolinePercent) {
        this.name = name;
        this.manufacturer = manufacturer;
        this.productionYear = productionYear;
        this.gasolinePercent = gasolinePercent;
    }

    public String getName() {
        return name;
    }

    public String getManufacturer() {
        return manufacturer;
    }

    public int getProductionYear() {
        return productionYear;
    }

    public int getGasolinePercent() {
        return gasolinePercent;
    }
}

Aggregate 的虚拟层亲代:UsedCarDealerAggregate

public interface UsedCarDealerAggregate {
    CarIterator createCarIterator();

    int size();

    Car getCar(int index);

    void add(Car car);
}

Iterator 的虚拟层亲代:CarIterator

public interface CarIterator {
    Car getFirst();

    Car getCurrent();

    Car getNext();

    boolean isDone();

    void reset();
}

Aggregate 的子代:UsedCarDealer

public class UsedCarDealer implements UsedCarDealerAggregate {
    private List<Car> list;

    public UsedCarDealer() {
        this.list = new ArrayList<>();
    }

    @Override
    public CarIterator createCarIterator() {
        return new UsedCarDealerIterator(this);
    }

    @Override
    public int size() {
        return list.size();
    }

    @Override
    public Car getCar(int index) {
        return list.get(index);
    }

    @Override
    public void add(Car car) {
        list.add(car);
    }
}

Iterator 的子代:UsedCarDealerIterator

public class UsedCarDealerIterator implements CarIterator {
    private UsedCarDealer usedCarDealer;
    private int currentIndex = 0;

    public UsedCarDealerIterator(UsedCarDealer usedCarDealer) {
        this.usedCarDealer = usedCarDealer;
    }

    @Override
    public Car getFirst() {
        return usedCarDealer.getCar(0);
    }

    @Override
    public Car getCurrent() {
        if (currentIndex < usedCarDealer.size()) {
            return usedCarDealer.getCar(currentIndex);
        } else {
            return null;
        }
    }

    @Override
    public Car getNext() {
        currentIndex++;

        if (currentIndex < usedCarDealer.size()) {
            return usedCarDealer.getCar(currentIndex);
        } else {
            return null;
        }
    }

    @Override
    public boolean isDone() {
        return currentIndex < usedCarDealer.size();
    }

    @Override
    public void reset() {
        currentIndex = 0;
    }

    public void showCurrentDetails() {
        Car currentCar = getCurrent();
        System.out.println("车辆的型号是 " + currentCar.getName() + " ,制造商则是 " + currentCar.getManufacturer());
        Calendar calendar = Calendar.getInstance();

        if ((calendar.get(Calendar.YEAR) - currentCar.getProductionYear()) > 10) {
            System.out.println("注意,车龄大於 10 年");
        }

        if (currentCar.getGasolinePercent() > 50) {
            System.out.println("油量足够\n");
        } else {
            System.out.println("油量不足,该加油了\n");
        }
    }
}

测试,输入车库内的车辆资料後,逐一检查:UsedCarDealerIteratorSample

public class UsedCarDealerIteratorSample {
    public static void main(String[] args) {
        UsedCarDealer sellingCars = new UsedCarDealer();
        sellingCars.add(new Car("RX450h", "Lexus", 2020, 100));
        sellingCars.add(new Car("M3", "BMW", 2015, 90));
        sellingCars.add(new Car("Camaro 2SS", "Chevrolet", 2017, 50));
        sellingCars.add(new Car("Continental Flying Spur", "Bentley", 2008, 10));
        sellingCars.add(new Car("Mustang", "Ford", 2020, 10));
        sellingCars.add(new Car("MGB", "MG", 1979, 5));
        sellingCars.add(new Car("Porsche", "Cayman S", 2014, 20));
        sellingCars.add(new Car("S60 T5", "Volvo", 2020, 15));
        sellingCars.add(new Car("Grand Cherokee Overland", "Jeep", 2017, 58));

        UsedCarDealerIterator carIterator = new UsedCarDealerIterator(sellingCars);
        while (carIterator.isDone()) {
            carIterator.showCurrentDetails();
            carIterator.getNext();
        }
    }
}

使用 JavaScript 实作

放入 Aggregate 内的物件:Car

class Car {
  /**
   * @param {string} name
   * @param {string} manufacturer
   * @param {number} productionYear
   * @param {number} gasolinePercent
   */
  constructor(name, manufacturer, productionYear, gasolinePercent) {
    this.name = name;
    this.manufacturer = manufacturer;
    this.productionYear = productionYear;
    this.gasolinePercent = gasolinePercent;
  }

  getName() {
    return this.name;
  }

  getManufacturer() {
    return this.manufacturer;
  }

  getProductionYear() {
    return this.productionYear;
  }

  getGasolinePercent() {
    return this.gasolinePercent;
  }
}

Aggregate 的虚拟层亲代:UsedCarDealerAggregate

/** @interface */
class UsedCarDealerAggregate {
  createCarIterator() { return; }

  size() { return; }

  /** @param {number} index */
  getCar(index) { return; }

  /** @param {Car} car */
  add(car) { return; }
}

Iterator 的虚拟层亲代:CarIterator

/** @interface */
class CarIterator {
  getFirst() { return; }

  getCurrent() { return; }

  getNext() { return; }

  isDone() { return; }

  reset() { return; }
}

Aggregate 的子代:UsedCarDealer

class UsedCarDealer extends UsedCarDealerAggregate {
  constructor() {
    super();
    /** @type {Car[]} */
    this.list = [];
  }

  /** @override */
  createCarIterator() {
    return new UsedCarDealerIterator(this);
  }

  /** @override */
  size() {
    return this.list.length;
  }

  /**
   * @override
   * @param {number} index
   */
  getCar(index) {
    return this.list[index];
  }

  /**
   * @override
   * @param {Car} car
   */
  add(car) {
    this.list.push(car);
  }
}

Iterator 的子代:UsedCarDealerIterator

class UsedCarDealerIterator extends CarIterator {
  /** @param {UsedCarDealer} usedCarDealer */
  constructor(usedCarDealer) {
    super();
    this.usedCarDealer = usedCarDealer;
    this.currentIndex = 0;
  }

  /** @override */
  getFirst() {
    return this.usedCarDealer.getCar(0);
  }

  /** @override */
  getCurrent() {
    if (this.currentIndex < this.usedCarDealer.size()) {
      return this.usedCarDealer.getCar(this.currentIndex);
    } else {
      return null;
    }
  }

  /** @override */
  getNext() {
    this.currentIndex++;

    if (this.currentIndex < this.usedCarDealer.size()) {
      return this.usedCarDealer.getCar(this.currentIndex);
    } else {
      return null;
    }
  }

  /** @override */
  isDone() {
    return this.currentIndex < this.usedCarDealer.size();
  }

  /** @override */
  reset() {
    this.currentIndex = 0;
  }

  showCurrentDetails() {
    const currentCar = this.getCurrent();
    console.log("车辆的型号是 " + currentCar.getName() + " ,制造商则是 " + currentCar.getManufacturer());
    const date = new Date();

    if ((date.getFullYear() - currentCar.getProductionYear()) > 10) {
      console.log("注意,车龄大於 10 年");
    }

    if (currentCar.getGasolinePercent() > 50) {
      console.log("油量足够\n");
    } else {
      console.log("油量不足,该加油了\n");
    }
  }
}

测试,输入车库内的车辆资料後,逐一检查:UsedCarDealerIteratorSample

const usedCarDealerIteratorSample = () => {
  const sellingCars = new UsedCarDealer();
  sellingCars.add(new Car("RX450h", "Lexus", 2020, 100));
  sellingCars.add(new Car("M3", "BMW", 2015, 90));
  sellingCars.add(new Car("Camaro 2SS", "Chevrolet", 2017, 50));
  sellingCars.add(new Car("Continental Flying Spur", "Bentley", 2008, 10));
  sellingCars.add(new Car("Mustang", "Ford", 2020, 10));
  sellingCars.add(new Car("MGB", "MG", 1979, 5));
  sellingCars.add(new Car("Porsche", "Cayman S", 2014, 20));
  sellingCars.add(new Car("S60 T5", "Volvo", 2020, 15));
  sellingCars.add(new Car("Grand Cherokee Overland", "Jeep", 2017, 58));

  const carIterator = new UsedCarDealerIterator(sellingCars);
  while (carIterator.isDone()) {
    carIterator.showCurrentDetails();
    carIterator.getNext();
  }
}

usedCarDealerIteratorSample();

总结

Iterator 模式可说是历史的轨迹,1994 年的时候,forEach() 的概念尚未成为许多程序语言的内建语法,因此需要特地开一个模式教导他人如何实作。时光飞逝,forEach() 的概念已经变成许多语言的内建语法,这时候来看 Iterator 模式显得有点尴尬,因为看了似乎也没学到什麽,不看内心觉得没有脚踏实地阅读「物件导向设计模式」。

想到唯一的用途是,其他资料结构需要实作 forEach() 功能的话,就需要这个模式了

程序语言都内建 Iterator 模式

明天将介绍 Behavioural patterns 的第五个模式:Mediator 模式。


<<:  面对拒绝

>>:  第21车厢-input全选/取消全选功能底加啦!

Day29 - 回顾这个月做的事情

回顾一下我们这个月到底做了些什麽 从IDE到最後上架 元件、属性使用 MVP 架构以及布局的介绍 ...

33岁转职者的前端笔记-DAY 23 JavaScript 变数与型别

Nan => Not a Number,要判断是不是NaN要用:isNaN(); 注意自动转...

身体惯性-螃蟹改不了横行

早起运动Day8 - 身体习惯​ ​ 不假思索的起床,做第一件事情以後就会带到第二件事情。​ 就像看...

[Day13] Android - Kotlin笔记:Parcelable & Serializable 与 SafeArgs的传递

这边简单介绍两者差异和选择: Parcelable: 效能比Serializable好,在记忆体开销...

电子发现参考模型(Electronic Discovery Reference Model)

-电子发现参考模型 证人(Witnesses )和证据(evidence)决定了司法结果。及时的电...