了解 forEach() 的來由。
簡單來說,實作任意資料結構的 forEach(),最常見的莫過於 Array、List 等,其他像是 Linked List、Stack、Queue、Tree 等也是可實作的資料結構,在模式內負責儲存的物件稱作 Aggregate。
這次的實作,採用 Array(JS)、 List(Java),放入名為 Car 的物件。
相關作法是:
Array(JS)、 List(Java) 方法,像是:
Array(JS)、 List(Java) 長度。Array(JS)、 List(Java) 負責儲存物件。Car 物件後,搭配 While 模擬 forEach()。以下範例以「依序檢查車庫內的二手車輛」為核心製作。
放入 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();
        }
    }
}
放入 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() 功能的話,就需要這個模式了
明天將介紹 Behavioural patterns 的第五個模式:Mediator 模式。