Javascript設計模式:工廠模式

之前對設計模式瞭解的不是很多,現在以前端爲切入點來學習設計模式,在學習的過程中會借鑑其他人的博客和代碼,會在文章末尾標註來源出處

1. 簡單工廠

根據參數的不同返回不同類的實例。簡單工廠模式專門定義一個類來負責創建其他類的實例,被創建的實例通常都具有共同的父類。

//汽車類
class Car {
    constructor({ doors, state, color }) {
        //默認值
        this.doors = doors || 4;
        this.state = state || 'brand new';
        this.color = color || 'silver';
    }
}
// 卡車類
class Truck {
    constructor({ state, wheelSize, color }) {
        this.state = state || 'used';
        this.wheelSize = wheelSize || 'large';
        this.color = color || 'blue';
    }
}
// 簡單工廠
class VehicleFactory {
    constructor() {
        this.vehicleClass = Car;
    }
    // 工廠方法 創建一個車型實例
    createVehicle(options) {
        switch (options.vehicleType) {
            case 'car':
                this.vehicleClass = Car;
                break;
            case 'truck':
                this.vehicleClass = Truck;
                break;
            // VehicleFactory.prototype.vehicleClass (Car)
        }

        return new this.vehicleClass(options);
    }
}
const carFactory = new VehicleFactory();
const car = carFactory.createVehicle({
    vehicleType: 'car',
    color: 'yellow',
    doors: 6,
});

適用環境

在以下情況下可以使用簡單工廠模式:

工廠類負責創建的對象比較少:由於創建的對象較少,不會造成工廠方法中的業務邏輯太過複雜。
客戶端只知道傳入工廠類的參數,對於如何創建對象不關心:客戶端既不需要關心創建細節,甚至連類名都不需要記住,只需要知道類型所對應的參數。

工廠模式的優點:

  • 簡單工廠模式通過這種做法實現了對責任的分割,它提供了專門的工廠類用於創建對象
  • 把所有實例化代碼集中在一起,簡化所用的類或在運行期間動態選擇所用類的工作
  • 弱化對象間的耦合 防止代碼重複 在一個方法中進行類的實例化,可以消除重複代碼,
  • 用一個對接口的調用取代一個具體的實現,有助於創建模塊化代碼

工廠模式的缺點:

  • 簡單工廠模式最大的問題在於工廠類的職責相對過重,增加新的產品需要修改工廠類的判斷邏輯,這一點與開閉原則是相違背的。
  • 由於工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都要受到影響。
  • 使用簡單工廠模式將會增加系統中類的個數,在一定程序上增加了系統的複雜度和理解難度。
  • 系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,在產品類型較多時,有可能造成工廠邏輯過於複雜,不利於系統的擴展和維護。
  • 簡單工廠模式由於使用了靜態工廠方法,造成工廠角色無法形成基於繼承的等級結構。

2. 抽象工廠

抽象工廠UML類圖


/**
 * 披薩原料工廠  接口
 * @class PizzaIngredientFactory
 */
class PizzaIngredientFactory {
    //該接口的方法必須覆蓋重寫,不允許子類繼承調用
    createDough() {
        throw new Error("This method must be overwritten!");
    }

    createSauce() {
        throw new Error("This method must be overwritten!");
    }

    createCheese() {
        throw new Error("This method must be overwritten!");
    }

    createVeggies() {
        throw new Error("This method must be overwritten!");
    }

    createPepperoni() {
        throw new Error("This method must be overwritten!");
    }

    createClam() {
        throw new Error("This method must be overwritten!");
    }
}
/**********食品材料類start*************/
class FreshClams {}
class Garlic {}
class MarinaraSauce {}
class Mushroom {}
class Onion {}
class RedPepper {}
class ReggianoCheese {}
class SlicedPepperoni {}
class ThinCrustDough {}
/**********食品材料類end*************/
/**
 * 紐約披薩原料工廠
 * @class PizzaIngredientFactory
 */
class NewYorkPizzaIngredientFactory extends PizzaIngredientFactory {
    createDough() {
        return new ThinCrustDough();
    }

    createSauce() {
        return new MarinaraSauce();
    }

    createCheese() {
        return new ReggianoCheese();
    }

    createVeggies() {
        return [new Garlic(), new Mushroom(), new RedPepper()];
    }

    createPepperoni() {}

    createClam() {}
}
// 披薩接口
class Pizza {
    constructor({
        name = "",
        dough = null,
        sauce = null,
        veggies = [],
        cheese = null,
        pepperoni = null,
        clams = null
    }) {
        this.name = name;
        this.dough = dough;
        this.sauce = sauce;
        this.veggies = veggies;
        this.cheese = cheese;
        this.pepperoni = pepperoni;
        this.clams = clams;
    }

    prepare() {
        throw new Error("This method must be overwritten!");
    }

    bake() {
        console.log("Bake for 25 minutes at 350");
    }

    cut() {
        console.log("Cutting the pizza into diagonal slices");
    }

    box() {
        console.log("Place pizza in official PizzaStore box");
    }

    getName() {
        return this.name;
    }

    setName(name) {
        this.name = name;
    }
}
/**************各種類披薩start**************/
class CheesePizza extends Pizza {
    constructor(style, ingredientFactory) {
        super({
            name: style + " Cheese Pizza"
        });
        console.log(this.name);
        this.ingredientFactory = ingredientFactory;
    }
    //
    prepare() {
        let ingredientFactory = this.ingredientFactory;
        console.log("Preparing " + this.name);
        this.dough = ingredientFactory.createDough();
        this.sauce = ingredientFactory.createSauce();
        this.cheese = ingredientFactory.createCheese();
    }
}
class ClamPizza extends Pizza {
    constructor(style, ingredientFactory) {
        super({
            name: style + " Clams Pizza"
        });
        this.ingredientFactory = ingredientFactory;
    }

    prepare() {
        let ingredientFactory = this.ingredientFactory;
        console.log("Preparing " + this.name);
        this.dough = ingredientFactory.createDough();
        this.sauce = ingredientFactory.createSauce();
        this.cheese = ingredientFactory.createCheese();
        this.clams = ingredientFactory.createClam();
    }
}
class PepperoniPizza extends Pizza {
    constructor(style, ingredientFactory) {
        super({
            name: style + " Pepperoni Pizza"
        });
        this.ingredientFactory = ingredientFactory;
    }

    prepare() {
        let ingredientFactory = this.ingredientFactory;
        console.log("Preparing " + this.name);
        this.dough = ingredientFactory.createDough();
        this.sauce = ingredientFactory.createSauce();
        this.cheese = ingredientFactory.createCheese();
    }
}
class VeggiePizza extends Pizza {
    constructor(style, ingredientFactory) {
        super({
            name: style + " Veggie Pizza"
        });
        this.ingredientFactory = ingredientFactory;
    }

    prepare() {
        let ingredientFactory = this.ingredientFactory;
        console.log("Preparing " + this.name);
        this.dough = ingredientFactory.createDough();
        this.sauce = ingredientFactory.createSauce();
        this.cheese = ingredientFactory.createCheese();
    }
}
/**************各種類披薩end**************/
const PIZZAS = {
    cheese: CheesePizza,
    veggie: VeggiePizza,
    clam: ClamPizza,
    pepperoni: PepperoniPizza
};
/********披薩商店接口*********/
class PizzaStore {
    createPizza() {
        throw new Error("This method must be overwritten!");
    }
    //統一接口
    orderPizza(type) {
        let pizza = this.createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
    }
}
/********披薩商店*********/
class NewYorkPizzaStore extends PizzaStore {
    //抽象方法
    createPizza(type) {
        let ingredientFactory = new NewYorkPizzaIngredientFactory();//
        let PizzaConstructor = PIZZAS[type];//選擇具體的披薩類型
        let pizza = null;
        // 選擇好具體的披薩之後,調用披薩工廠方法選擇披薩的食品材料
        if (PizzaConstructor) {
            pizza = new PizzaConstructor("New York Style", ingredientFactory);
        }
        return pizza;
    }
}
var oPizzaStore = new NewYorkPizzaStore();
oPizzaStore.orderPizza("cheese");

抽象工廠優點:

  • 抽象工廠模式隔離了具體類的生成,使得客戶並不需要知道什麼被創建。應用抽象工廠模式可以實現高內聚低耦合的設計目的,因此抽象工廠模式得到了廣泛的應用。
  • 當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象。這對一些需要根據當前環境來決定其行爲的軟件系統來說,是一種非常實用的設計模式。
  • 增加新的具體工廠和產品族很方便,無須修改已有系統,符合“開閉原則”。

抽象工廠優點:

  • 在添加新的產品對象時,難以擴展抽象工廠來生產新種類的產品,
  • 開閉原則的傾斜性(增加新的工廠和產品族容易,增加新的產品等級結構麻煩)。

工廠模式的使用場景

  • jquery $(“div”)
class jQuery{
    constructor(selector){}
    append(node){}
    addClass(name){}
    html(){}
}
window.$=function(selector){
    return new jQuery(selector)
}
  • React.createElement ()
class Vnode{
    //...省略代碼
}
React.createElement = function(tag,attrs,children){
    return new Vnode(tag, attrs, children);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章