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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容