这是新的javascript工厂模式吗?

Vor*_*ord 5 javascript design-patterns

合理的警告-很久以前,我写了很多C ++,无奈将javascript强制化为我当时熟悉的设计模式。可以在任何答复中指责我愚昧无误;-)


在当前项目中,我想按名称创建对象,该名称指示工厂模式。因此,我阅读了Google命中“ JavaScript工厂模式”的首页。他们都有共同的丑陋之处:

if (name === 'FactoryPartA') {
    parentClass = PartA;
} else if (name === 'FactoryPartB') {
    parentClass = PartB;
} else if ...
    parentClass = PartZ;
}

return new parentClass();
Run Code Online (Sandbox Code Playgroud)

其中有两个问题:

  1. 每当我为工厂创建一个新零件时,我都必须编辑工厂的实现,并且我希望避免工作和错误插入机会。
  2. 这是一个硬编码的线性搜索,几乎没有尖叫“效率!”

因此,这就是我的想法-模块和工厂模式的结合以及模块模式的信息隐藏优点,该信息隐藏策略是通过在工厂的工厂内定义工厂零件的类别的策略提供的register

最后,我要提出一个问题:我不敢相信,比我更好的编码人员以前没有做到这一点,因此,如果知道的话,请共享一个指向工厂模式此规范的规范版本的链接。

注意:在此示例中,我一起运行了所有代码。在我的项目中,工厂,FactoryPartA,FactoryPartB和客户端代码都位于单独的文件中。

namespace('mynamespace');

// object factory
mynamespace.factory = (function () {
    'use strict';
    var api = {};
    var registry = [];

    // register an item
    api.register = function (item) {
        if (registry.some (function (r) {return r.name === item.name;})) { 
            throw new Error ('factory.register(): name collision detected: ' + name);
        } else {
            registry.push(item);
        }
    };

    // make an item given its name
    api.make = function (name) {
        var item = null;
        var idx = registry.findIndex (function (r) {
            return r.name === name;
        });
        if (idx >= 0) {
            item = new registry[idx].make();
        }
        return item;
    };

    return api;

})();


// define a module & register it with factory
mynamespace.factory.register ({
    name: 'FactoryPartA',
    make: function FactoryPartA () {
        'use strict';
        var label = 'Factory Part A';   // private property

        this.test = undefined;  // public property

        this.label = function () {   // public method
            return label;
        };

        return this;
    }
});

// define a different module & register it with factory
mynamespace.factory.register ({
    name: 'FactoryPartB',
    make: function FactoryPartB () {
        'use strict';
        var label = 'Factory Part B';

        this.test = undefined;

        this.label = function () {
            return label;
        };

        return this;
    }
});

// client code
var aPart = mynamespace.factory.make('FactoryPartA');
var bPart = mynamespace.factory.make('FactoryPartB');

console.log (aPart.label()); // logs 'Factory Part A'
console.log (bPart.label()); // logs 'Factory Part B'

var anotherPart = mynamespace.factory.make('FactoryPartA');
aPart.test = 'this one is not';
anotherPart.test = 'the same as this one';
console.log (aPart.test !== anotherPart.test); // logs true
Run Code Online (Sandbox Code Playgroud)

jed*_*ung 6

要回答一个基本问题-这是一个新的Javascript工厂模式-不,不是。(查看ES6 / ES2015Typescript和Aurelia的依赖项注入模块。)

现在,要回答总体声明-您实际上是在尝试将元数据添加到Javascript的“类”类型中。事实是,您似乎正在尝试创建工厂工厂-我不确定您是否需要。(也许您已经简化了示例。)

对于您的示例,我会做更多类似的事情:

namespace('mynamespace');

// object factory
mynamespace.factory = (function () {
    'use strict';
    var api = {};
    var registry = {};

    // register an item
    api.register = function (name, item, overwrite) {
        if (!overwrite || registry.hasOwnProperty('name')) { 
            throw new Error ('factory.register(): name collision detected: ' + name);
        }

        registry[name] = item;
    };

    // make an item given its name
    api.make = function (name) {
        var item = registry[name];
        return item ? new item() : null; // or better, Object.create(item);
    };

    return api;

})();


// define a module & register it with factory
mynamespace.factory.register ('FactoryPartA', function FactoryPartA () {
    'use strict';
    var label = 'Factory Part A';   // private property

    this.test = undefined;  // public property

    this.label = function () {   // public method
        return label;
    };
});

// define a different module & register it with factory
mynamespace.factory.register ('FactoryPartB', function FactoryPartB () {
    'use strict';
    var label = 'Factory Part B';

    this.test = undefined;

    this.label = function () {
        return label;
    };
});
Run Code Online (Sandbox Code Playgroud)

这将删除多余的工厂物料,因此它不是FactoryFactory。(不确定为什么要这样做。)此外,您提到了线性搜索-通过使用对象而不是数组,可以避免线性查找(对象哈希是恒定时间查找)。最后-您实际上可以注册任何名称,而不必将其放入包装对象中。这更接近DI。

但是,如果您确实想执行元数据样式的方法,则可以执行以下操作:

namespace('mynamespace');

// object factory
mynamespace.factory = (function () {
    'use strict';
    var api = {};
    var registry = {};

    // register an item
    api.register = function (item, overwrite) {
        if (!overwrite || registry.hasOwnProperty(item.name)) { 
            throw new Error ('factory.register(): name collision detected: ' + item.name);
        }

        registry[item.name] = item;
    };

    // make an item given its name
    api.make = function (name) {
        var item = registry[name];
        return item ? new item() : null; // or better, Object.create(item);
    };

    return api;

})();


// define a module & register it with factory
mynamespace.factory.register (function FactoryPartA () {
    'use strict';
    var label = 'Factory Part A';   // private property

    this.test = undefined;  // public property

    this.label = function () {   // public method
        return label;
    };
});

// define a different module & register it with factory
mynamespace.factory.register (function FactoryPartB () {
    'use strict';
    var label = 'Factory Part B';

    this.test = undefined;

    this.label = function () {
        return label;
    };
});
Run Code Online (Sandbox Code Playgroud)

这使用函数名称而不是单独的属性。(函数可以用Javascript命名或匿名。此方法不适用于匿名函数。)我在上面提到了Typescript,因为当Typescript编译为Javascript时,它实际上将许多自己的元数据放置在对象上,类似于您正在做的事情。我提到Aurelia的依赖项注入是因为它的@autoinject功能实际上是读取此元数据来创建对象的,与您正在执行的操作类似。

但实际上...。除非您尝试创建一个依赖项注入容器(它比示例中需要更多的逻辑-您想要一个容器,它的键也可以指向实例),我认为您会得到更多的功能出来的Object.create()Object.assign()比你会与这种模式。在该环境之外,无需使用静态类型,强类型,编译语言所需要的许多设计模式。因此,您可以执行以下操作:

function PartA () {
    'use strict';
    var label = 'Factory Part A';   // private property

    this.test = undefined;  // public property

    this.label = function () {   // public method
        return label;
}

function PartB () {
    'use strict';
    var label = 'Factory Part B';

    this.test = undefined;

    this.label = function () {
        return label;
    };
}

var A = Object.create(PartA);
var B = Object.create(PartB);
Run Code Online (Sandbox Code Playgroud)

简单来说,这要容易得多。