了解 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 模式。