JS设计模式7 - 工厂与抽象工厂

工厂模式

工厂模式

目的

设计一个创建对象的方法,让派生类控制对象创建的过程。

何时使用

  1. 不知道要创建的具体类
  2. 派生类指定具体的创建过程和细节
  3. 父类希望延迟创建到派生类

举例

很多系统都有用户和用户组的概念,比如linux系统,当系统想创建一个用户的时候,系统会把创建的工作交给各个具体的用户实现类。父类会处理所有的公共操作,派生类则会定一个工厂方法来处理特殊的操作。系统可能又AdminUser和StandardUser,但是他们都派生于UserObject。AdminUser在创建的时候可能会做一些额外的操作确保自己的权限比较大。

代码

/**
 * @author: Mark Gable
 * @fileOverview: An example of the Factory design pattern
 */

// http://codepen.io/mgable43/pen/rsjGC

/** @namespace */
var myPizzaFactory = (function () {
    "use strict";
    /**
     * simplePizzaFactory constructs a simplePizzaFactory object
     * @param {string} type pizza type
     * @method createPizza
     * @returns {object} pizza object
     */
    var simplePizzaFactory = function  () {
            return {
                createPizza : function (type) {
                    var pizza = "unknown";
                    if (type === "cheese") {
                        pizza = cheesePizza();
                    }else if (type === "pepperoni") {
                        pizza = pepperoniPizza();
                    }
                    return pizza;
                }
            }
        },

        /**
         * pizzaStore constructs a pizzaStore object
         * @param {object} factory simplePizzaFactory object
         * @method orderPizza
         * @returns {object} pizza object
         * @note This is an example of a Factory design pattern
         */
        pizzaStore = function  (factory) {
            return {
                orderPizza : function (type) {
                    var pizza = factory.createPizza (type);
                    console.info ( pizza.prepare() );
                    console.info ( pizza.bake() );
                    console.info ( pizza.cut() );
                    console.info ( pizza.box() );
                    return pizza;
                }
            }
        },

        /**
         * pizza constructs a pizza object
         * @param {object} specs specifies pizza characteristics
         * @returns {object} pizza object
         */
        pizza = function (specs){
            return {
                name: specs.name || "unknown",
                dough: specs.dough || "unknown",
                sauce: specs.sauce || "unknown",
                prepare: specs.prepare || function () { return "Preparing " + this.name },
                bake: specs.bake || function () { return "Bake for 25 minutes at 350" },
                cut: specs.cut || function () { return "Cutting pizza into diagonal slices" },
                box: specs.box || function () { return "Place pizza in box" },
            }
        },

        /**
         * cheesePizza constructs a cheesePizza object
         * @returns {object} cheese pizza object
         */
        cheesePizza = function () {
            return pizza ({
                name: "cheese pizza",
                dough: "thin crust",
                sauce: "Marinara sauce",
                cut: function () { return "Cutting the pizza into squares" }
            });
        },

        /**
         * pepperoniPizza constructs a pepperoniPizza object
         * @returns {object} pepperoni pizza object
         */
        pepperoniPizza = function () {
            return pizza ({
                name: "pepperoni pizza",
                dough: "thick crust",
                sauce: "Plum sauce",
                bake: function () { return "Bake for 30 minutes at 325" }
            });
        };

    var store = pizzaStore(simplePizzaFactory());
    store.orderPizza ("cheese");
    store.orderPizza ("pepperoni");
})();

上面的代码实现了工厂模式的思想,simplePizzaFactory可以用来创建不同的Pizza,它并不知道如何创建,而是把具体的操作交给cheesePizza和pepperoniPizza。

当使用JavaScript语言来实现设计模式的时候,往往不需要拘泥于静态语言的继承方式,相反,我们应该关注思想本身。

抽象工厂

抽象工厂

目的

提供接口创建系列产品

何时使用

  1. 创建的对象和使用它们的系统是分离的
  2. 需要创建的对象是家族式的
  3. 创建的众多对象是在一起使用的
  4. 具体创建对象的类和系统解耦

举例

在游戏中往往需要创建大量对象,下面的代码需要创建巫师和剑士,他们自己分别又包含很多对象。

代码

const factoryType = {
    swordsman: "swordsman",
    wizard: "wizard"
};

class IHeroFactory {
    createAbilities() {
    }

    createEquipment() {
    }

    createSkills() {
    }
}

class SwordsmanFactory extends IHeroFactory {
    createAbilities() {
        return new SwordsmanAbility();
    }

    createEquipment() {
        return new SwordsmanEquipment();
    }

    createSkills() {
        return new SwordsmanSkill();
    }
}

class WizardFactory extends IHeroFactory {
    createAbilities() {
        return new WizardAbilitiy();
    }

    createEquipment() {
        return new WizardEquipment();
    }

    createSkills() {
        return new WizardSkill();
    }
}

class FactoryMaker {
    static getHeroFactory(type) {
        var factory = null;
        switch(type) {
            case factoryType.wizard:
                factory = new WizardFactory();
                break;
            case factoryType.swordsman:
                factory = new SwordsmanFactory();
                break;
        }
        return factory;
    }
}

class IAbstractSkill {
    getMainSkill() {
    }
    getSecondarySkill() {
    }
}

class SwordsmanSkill extends IAbstractSkill {
    constructor() {
        super();
        this._mainSkillName = "slash";
        this._secondarySkillType = "berserk";
    }

    getMainSkill() {
        return this._mainSkillName;
    }

    getSecondarySkill() {
        return this._secondarySkillType;
    }
}

class WizardSkill extends IAbstractSkill {
    constructor() {
        super();
        this._mainSkillName = "fireball";
        this._secondarySkillType = "tornado";
    }

    getMainSkill() {
        return this._mainSkillName;
    }

    getSecondarySkill() {
        return this._secondarySkillType;
    }
}

class IAbstractEquipment {
    getEquipment() {
    }
}

class SwordsmanEquipment extends IAbstractEquipment {
    constructor() {
        super();
        this._equipment = {
            type: "Robe of the Chaos",
            armor: 20,
            resistance: 100
        }
    }

    getEquipment() {
        return this._equipment;
    }
}

class WizardEquipment extends IAbstractEquipment {
    constructor() {
        super();
        this._equipment = {
            type: "Wrath of the Lich King",
            armor: 3,
            extraIntelligence: 5,
            extraMP: 100
        }
    }

    getEquipment() {
        return this._equipment;
    }
}

class IAbstractAbility {
    getAbilities() {
    }
}

class SwordsmanAbility extends IAbstractAbility {
    constructor() {
        super();
        this._heroProperties = {
            strength: 10,
            agility: 5,
            extraPower: true,
            extraPowerLevel: 1
        }
    }
    getAbilities() {
        return this._heroProperties;
    }
}

class WizardAbilitiy extends IAbstractAbility {
    constructor() {
        super();
        this._heroProperties = {
            strength: 10,
            intelligence: 30,
            agility: 5
        };
    }

    getAbilities() {
        return this._heroProperties;
    }
}



(function run () {
    let wizardFactory = FactoryMaker.getHeroFactory(factoryType.wizard),
        swordsmanFactory = FactoryMaker.getHeroFactory(factoryType.swordsman);

    let wiz = {
        abilities: wizardFactory.createAbilities(),
        equipment: wizardFactory.createEquipment(),
        skills: wizardFactory.createSkills()
    };

    let swrd = {
        abilities: swordsmanFactory.createAbilities(),
        equipment: swordsmanFactory.createEquipment(),
        skills: swordsmanFactory.createSkills()
    };

    let testHero = {
        abilities: wizardFactory.createAbilities(),
        equipment: wizardFactory.createEquipment(),
        skills: swordsmanFactory.createSkills()
    };

    console.log(wiz, swrd, testHero);
})();

上面的代码有两个工厂,一个是男巫(wizard),一个是剑客。创建这两个对象的时候还需要给他们赋予能力,装备和技能。这些对象都是相关的。这里的代码模拟了Java,提供了IHeroFactory接口,可以看到这个接口可以创建三个对象。

结论

工厂方法让我们摆脱new操作符,这样我们就不需要依赖于具体的实现类。抽象工厂让我们可以创建相关的一系列产品。抽象工厂包装了独立的工厂,但是这些工厂创建的产品是在一起使用的。
https://github.com/benhaben/essentialjsdesignpatterns.git

推荐阅读更多精彩内容