【公众号:CS阿吉】
简单工厂模式,又称为静态工厂方法模式(Static Factory Method Pattern),属于「创建型设计模式」。
创建型设计模式是一类处理对象创建的设计模式,通过某种方式控制对象的创建来避免基本对象创建时可能导致设计上的问题或增加设计上的复杂度。
简单工厂模式不在GoF 23种设计模式之列。
1. 定义
简单工厂模式:根据参数的不同返回不同类的实例。
核心思想:创建单一对象。
2. 背景
提供一个工厂类,传入特定参数,返回特定产品(特定实例对象)。
举例子,比如,按钮工厂,通过传入不同参数,返回不同的按钮实例,如警告按钮-warn,成功按钮-success,提示按钮-toast等等。
3. UML类图
4. 通用写法
4-1. 对不同的类实例化
通过类创建的对象,继承同一个父亲,父亲原型上的方法可以共用。
此处的productCommonMethod是产品继承来的。
class SimpleFactory {
constructor(type, content) {
this.type = type;
this.content = content;
};
static classConst = {
PRODUCT_A: 'ProductA',
PRODUCT_B: 'ProductB',
PRODUCT_C: 'ProductC'
};
makeProduct() {
switch(this.type) {
case classConst.PRODUCT_A:
return new ProductA();
case classConst.PRODUCT_A:
return new ProductB();
case classConst.PRODUCT_A:
return new ProductC();
}
}
}
class IProduct {
constructor(type, content) {
this.type = type;
this.content = content;
}
productCommonMethod() {
console.log('IProduct -- productCommonMethod');
}
}
class ProductA extends IProduct{
constructor() {
super('ProductA', 'this is ProductA');
}
doSpecificThingA() {
console.log('ProductA -- doSpecificthing');
}
}
class ProductB extends IProduct{
constructor() {
super('ProductB', 'this is ProductB');
}
doSpecificThingB() {
console.log('ProductB -- doSpecificthing');
}
}
class ProductC extends IProduct{
constructor() {
super('ProductC', 'this is ProductC');
}
doSpecificThingC() {
console.log('ProductC -- doSpecificthing');
}
}
4-2. 创建相似对象
创建一个新对象,扩展其方法和属性,将其返回。
此处类似于「寄生继承」,但新对象没有继承任何类或对象。 此处的productCommonMethod是每一个产品都有自己的productCommonMethod。
function SimpleFactory(type, context) {
const classConst = {
PRODUCT_A: 'ProductA',
PRODUCT_B: 'ProductB',
PRODUCT_C: 'ProductC'
};
let o = new Object();
o.type = type;
o.context = context;
o.productCommonMethod = function() {
console.log('IProduct -- productCommonMethod');
};
if(type === classConst.PRODUCT_A) {
o.doSpecificThingA = function() {
console.log('ProductA -- doSpecificthing');
};
}
if(type === classConst.PRODUCT_B) {
o.doSpecificThingB = function() {
console.log('Productb -- doSpecificthing');
};
}
if(type === classConst.PRODUCT_C) {
o.doSpecificThingC = function() {
console.log('ProductC -- doSpecificthing');
};
}
return o;
}
5. 优点
(1)无需关心创建细节,将对象的创建和对象的使用分离。
(2)结构简单,调用方便。
(3)工厂类和产品职责明确。
6. 缺点
(1)工厂类的职责过重,增加新产品需要修改工厂的判断逻辑,即扩展困难,同时违背开闭原则。
(2)产品无法具备继承能力。
(3)工厂类单一,当产品种类增多时,工厂类代码臃肿,违背高聚合原则。
7. 应用
根据提供一个工厂类,传入特定参数,返回特定产品(特定实例对象)
,Vue框架的虚拟DOM节点和DOM节点的创建、React框架DOM节点的创建,都是根据输入参数的不同,前者返回不同的VNode和DOM,后者返回不同的DOM节点。采用的编码方式都是参照4-2 创建相似对象
。
7-1. Vue 虚拟DOM节点的创建 和 DOM节点的创建
// 1. Vue根据tag的不同,创建不同的VNode节点
export function _createElement (
context,
tag, // string | Class<Component> | Function | Object
data, // VNodeData
children
){
let vnode, ns
if (typeof tag === 'string') {
vnode = new VNode( tag, data, children, undefined, undefined, context)
} else {
// direct component options / constructor
vnode = createComponent(tag, data, context, children)
}
if (Array.isArray(vnode)) {
return vnode
} else if (isDef(vnode)) {
if (isDef(ns)) applyNS(vnode, ns)
if (isDef(data)) registerDeepBindings(data)
return vnode
} else {
return createEmptyVNode()
}
}
// 2. Vue根据vnode类型的不同,创建不同的DOM节点
// 根据vnode的type不同,对elm属性调用不同函数进行赋值,同时分别进行特定行为,进而生成不同的DOM节点。
function createElm (
vnode,
insertedVnodeQueue,
parentElm,
refElm
) {
var data = vnode.data;
var children = vnode.children;
var tag = vnode.tag;
if (isDef(tag)) { // 普通元素,如div、p
vnode.elm = vnode.ns
? nodeOps.createElementNS(vnode.ns, tag)
: nodeOps.createElement(tag, vnode);
setScope(vnode);
/* istanbul ignore if */
{
createChildren(vnode, children, insertedVnodeQueue);
if (isDef(data)) {
invokeCreateHooks(vnode, insertedVnodeQueue);
}
insert(parentElm, vnode.elm, refElm);
}
if (data && data.pre) {
creatingElmInVPre--;
}
} else if (isTrue(vnode.isComment)) { // 注释DOM
vnode.elm = nodeOps.createComment(vnode.text);
insert(parentElm, vnode.elm, refElm);
} else { // 文本DOM
vnode.elm = nodeOps.createTextNode(vnode.text);
insert(parentElm, vnode.elm, refElm);
}
}
7-2. React DOM节点的创建
export function render(
element,
container,
callbackn,
) {
invariant(
isValidContainerLegacy(container),
'Target container is not a DOM element.',
);
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
function legacyRenderSubtreeIntoContainer(
parentComponent,
children,
container,
forceHydrate,
callback
) {
if (__DEV__) {
topLevelUpdateWarnings(container);
warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
}
let root = container._reactRootContainer;
let fiberRoot;
if (!root) {
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
);
fiberRoot = root;
// 这里是判断接受的是组件类型,将虚拟dom转成真实dom
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
}
unbatchedUpdates(() => {
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
fiberRoot = root;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
}
updateContainer(children, fiberRoot, parentComponent, callback);
}
// 将dom挂在到页面的父节点上
return getPublicRootInstance(fiberRoot);
}