iT邦幫忙

2021 iThome 鐵人賽

DAY 21
0
Software Development

也該是時候學學 Design Pattern 了系列 第 21

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


上一篇
Day 20: Behavioral patterns - Interpreter
下一篇
Day 22: Behavioral patterns - Mediator
系列文
也該是時候學學 Design Pattern 了31

尚未有邦友留言

立即登入留言