JavaScript Structural Pattern For Code 開箱 JavaScript 設計模式 (2-2)

me
林彥成
2023-04-02 | 2 min.
文章目錄
  1. 1. Adapter Pattern
  2. 2. Bridge Pattern
  3. 3. Facade Pattern
  4. 4. Flyweight Pattern

上一篇文章介紹過了優化語法的四種結構型設計模式,這篇文章主要會從優化程式碼結構的幾種 Pattern 開始介紹

接下來會繼續從優化程式碼結構的四種結構型設計模式開始介紹

  • Adapter Pattern
  • Bridge Pattern
  • Facade Pattern
  • Flyweight Pattern

Design Patterns 依照目的分成三群:

Adapter Pattern

在這個範例中,有兩個介面

  • MotorcycleFactory 代表原有的機車工廠
  • NewMotorcycleFactory 代表新的機車工廠

新的機車工廠的製造方式不同 (produce -> create),所以透過 NewMotorcycleFactoryAdapter 多轉一層,讓原有的程式在不改變寫法的情況下,可以使用新的機車工廠物件 (新的方法) 去製造機車。

// 機車工廠原有的介面
class MotorcycleFactory {
  constructor(name) {
    this.name = name;
  }
  produce() {
    console.log(`${this.name} 生產了一輛機車`);
  }
}

// 新增的介面
class NewMotorcycleFactory {
  constructor(name) {
    this.name = name;
  }
  create() {
    console.log(`${this.name} 創建了一輛機車`);
  }
}

// Adapter
class NewMotorcycleFactoryAdapter extends MotorcycleFactory {
  constructor(newMotorcycleFactory) {
    super(newMotorcycleFactory.name);
    this.newMotorcycleFactory = newMotorcycleFactory;
  }
  produce() {
    return this.newMotorcycleFactory.create();
  }
}

// 使用
let newMotorcycleFactory = new NewMotorcycleFactory("新機車工廠");
let adapter = new NewMotorcycleFactoryAdapter(newMotorcycleFactory);

adapter.produce();

Bridge Pattern

假設你想要生產機車,會需要像是引擎跟底盤等等屬性。

當如果要增加引擎、底盤的種類來組出新款機車,就需要 Bridge pattern,讓機車實作後只需要呼叫 Bridge 介面的方法即可。

總結來說,Bridge pattern 可以幫助你在物件的實作和介面之間建立 Bridge 關係,讓你可以更靈活地應對系統的變化。

class SportBikeEngine {
  start() {
    console.log("Starting sport bike engine...");
  }
}

class CruiserEngine {
  start() {
    console.log("Starting cruiser engine...");
  }
}

class SportBikeChassis {
  ride() {
    console.log("Riding sport bike chassis...");
  }
}

class CruiserChassis {
  ride() {
    console.log("Riding cruiser chassis...");
  }
}

// Motorcycle Abstraction
class Motorcycle {
  constructor(brand, engineType, chassisType) {
    this.brand = brand;
    this.engineType = engineType;
    this.chassisType = chassisType;
  }

  start() {
    this.engineType.start();
  }

  ride() {
    this.chassisType.ride();
  }
}

class BridgeBike extends Motorcycle {
  constructor(brand, engineType, chassisType) {
    super(brand, engineType, chassisType);
  }

  riding() {
    this.ride();
  }
}

// Usage
const sportBikeEngine = new SportBikeEngine();
const cruiserEngine = new CruiserEngine();

const sportBikeChassis = new SportBikeChassis();
const cruiserChassis = new CruiserChassis();

const sportBike = new BridgeBike("Honda", sportBikeEngine, sportBikeChassis);
const cruiser = new BridgeBike("SYM", cruiserEngine, cruiserChassis);
const mixedMotor = new BridgeBike("Custom", sportBikeEngine, cruiserChassis);

sportBike.start();
sportBike.riding();

cruiser.start();
cruiser.riding();

mixedMotor.start();
mixedMotor.riding();

Facade Pattern

你各位知道嗎?! React 最常用的竟然用的就是 Facade Pattern!

是把一堆難懂的東西,包裝成一個簡單易懂的東西,讓使用者用起來更方便。

以單純的 JavaScript 範例來說,Google Map API 就是一個 Facade Pattern,透過一個 Map 物件把複雜的圖層與地理資訊操作封裝起來。

let map;

function initMap() {
  map = new google.maps.Map(document.getElementById("map"), {
    center: { lat: -34.397, lng: 150.644 },
    zoom: 8,
  });
}

window.initMap = initMap;

React 的元件跟 Hooks 也是異曲同工之妙,useSWR 來說就封裝了 data、error、loading 的邏輯。

而元件則是把實作都包裝在元件中,我們只需要關注傳入的 props 就可以運用元件。

const { data, error, isLoading } = useSWR("/api/user", fetcher);

<BreadCrumb model={items} home={home} />;

Flyweight Pattern

當需要大量創建相同或類似的物件時,可以使用 Flyweight Pattern 模式,讓這些物件共用一個記憶體空間,從而減少記憶體的使用量。

其實也就是之前提到的單體模式的延伸

class Motorcycle {
  constructor(brand, model, color) {
    if (instance) return instance;
    if (!instance) {
      this.brand = brand;
      this.model = model;
      this.color = color;
      instance = this;
    }
  }
}

可以進一步的建立機車工廠來做一個 Flyweight Pattern

const MotorcycleFactory = (function () {
  const motorcyclePool = {};

  function getMotorcycle(brand, model, color) {
    const key = brand + model + color;

    if (!motorcyclePool[key]) {
      motorcyclePool[key] = new Motorcycle(brand, model, color);
    }

    return motorcyclePool[key];
  }

  return {
    getMotorcycle: getMotorcycle,
  };
})();

const motorcycle1 = MotorcycleFactory.getMotorcycle("Yamaha", "NMAX", "Black");
const motorcycle2 = MotorcycleFactory.getMotorcycle("Honda", "PCX", "Red");
const motorcycle3 = MotorcycleFactory.getMotorcycle("Yamaha", "NMAX", "Black");

喜歡這篇文章,請幫忙拍拍手喔 🤣


share