工厂(Factory)
工厂的目的就是创建对象。它通常用一个类或者类的静态方法实现,有以下几个目的:
- 重复执行创建相似对象的操作
- 提供一种方法让工厂的使用者在编译时创建对象而不用知道具体的类型(class)
第二点在静态类语言(static class languages)中更加重要,它可能对创建未知类的(在编译期间)实例是非常重要的。在JavaScript中,这部分的实现是非常简单的。
工厂方法创建的对象(或类)故意设计从相同的parent对象继承;他们是具体的实现了特定功能的子类。有时共用的parent就是包含工厂方法的类。
让我们看一个实现的例子,我们有:
- 一个通用的parent CarMaker 构造函数
- 一个CarMaker的静态方法叫做factory(),用来创建car对象
- 专门的构造函数CarMaker.Compact, CarMaker.SUV和CarMaker.Convertible继承至CarMaker。它们将全部被定义为parent的静态属性以便保持全局命名空间的clean,并且当我们需要的时候我们知道去哪找它们。
首先让我看一下完整的实现将如何被使用:
var corolla = CarMaker.factory('Compact'); var solstice = CarMaker.factory('Convertible'); var cherokee = CarMaker.factory('SUV'); corolla.drive(); // "Vroom, I have 4 doors" solstice.drive(); // "Vroom, I have 2 doors" cherokee.drive(); // "Vroom, I have 17 doors"
这部分:
var corolla = CarMaker.factory('Compact');
可能是工厂模式中最容易识别的(most recognizable)的部分。你有一个方法在运行时接收一个作为字符串传递的类型然后创建并返回请求类型的对象。
看不到使用new调用构造方法或任何对象字面量,仅仅有一个创建基于字符串指定类型的对象的函数。
这里有一个工作模式的例子实现,可以让前面的代码块正常工作:
// parent constructor function CarMaker() {} // a method of the parent CarMaker.prototype.drive = function() { return "Vroom, I have " + this.doors + " doors"; }; // the static factory method CarMaker.factory = function(type) { var constr = type, newcar; // error if the constructor doesn't exist if (typeof CarMaker[constr] !== "function") { throw { name: "Error", message: constr + " doesn't exist" }; } // at this point the constructor is known to exist // let's have it inherit the parent but only once if (typeof CarMaker[constr].prototype.drive !== "function") { CarMaker[constr].prototype = new CarMaker(); } // create a new instance newcar = new CarMaker[constr](); // optionally call some methods and then return... return newcar; }; // define specific car makers CarMaker.Compact = function() { this.doors = 4; }; CarMaker.Convertible = function() { this.doors = 2; }; CarMaker.SUV = function() { this.doors = 24; };
关于工厂模式的实现没有什么特别难。你所需要的就是查找创建需要类型的构造函数创建一个对象。在这个例子中,一个简单的命名规范被用来映射(map)对象类型和创建他们的构造方法。继承部分仅仅是常见的重复代码段的一个例子可以放到工厂方法中而不用每个类型的构造方法都重复一次。
内置的对象工厂(Built-in Object Factory)
一个"factory in the wild"例子之后,考虑一下内置的全局Object()构造函数。它也起到工厂的作用,因为它能创建不同的对象,取决于传入的参数。如果你传递一个原始的数字,它在背后可以创建一个用Number()构造函数创建的对象。这对string和boolean类型也是一样的。其它任何值,包括不传入值,将会创建一个普通对象。
这里有一些例子和行为测试。注意Object可以用new调用也可以不用:
var o = new Object(), n = new Object(1), s = Object('1'), b = Object(true); // test o.constructor === Object; // true n.constructor === Number; // true s.constructor === String; // true b.constructor === Boolean; // true he fact that Object() is also a factory is of littl
事实上Object也是一个工厂但几乎没有什么实际用途,仅仅作为一个说明工厂模式在我们身边的例子值得一提。