IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> JS设计模式 - 工厂模式 -> 正文阅读

[JavaScript知识库]JS设计模式 - 工厂模式

《JavaScript设计模式》工厂模式学习小结

一、定义解释

因为用new关键字和类构造函数创建对象,会导致两个相关的类之间产生依赖性,于是就出现了工厂模式来帮我们。

工厂模式用一个方法来决定实例化哪个具体的类,有助于消除两个类之间的依赖模式。它分为简单工厂模式和复杂工厂模式。

简单工厂模式:使用另外一个类或对象封装实例化操作(通常是一个单体)。

复杂工厂模式:实现一个抽象工厂方法并把实例化工作推迟到子类中,即使用子类来决定一个成员变量应该是哪个具体的类的实例。

二、举例解释

假设我们要开几个自行车店,店里有几种不同型号自行车出售。

🚄我们可以这样写:

var BicycleShop = function(){};
BicycleShop.prototype = {
    sellBicycle: function(model){
        var bicycle;
        switch(model){
            case 'Speedster':
                bicycle = new Speedster();
                break;
            case 'Lowrider':
                bicycle = new Lowrider();
                break;
            case 'ComfortCruiser':
            default:
                bicycle = new ComfortCruiser();
        }
        Interface.ensureImplements(bicycle, Bicycle);
        bicycle.assemble(); // 组装
        bicycle.wash(); // 清洗
        return bicycle;
    }
}
var shop1 = new BicycleShop();
var newBike = shop1.sellBicycle('Speedster')

但是,如果这个时候我们要加入新的型号,我们就得修改 BicycleShop 代码,即使这个 BicycleShop 的功能没变(创建自行车新实例,组装,清洗,交给客户)。所以更好的方法是把“创建新实例”这部分工作交给一个简单工厂对象。

🚄改成简单工厂模式后的代码:

var BicycleFactory = {
    createBicycle: function(model){
        var bicycle;
        switch(model){
            case 'Speedster':
                bicycle = new Speedster();
                break;
            case 'Lowrider':
                bicycle = new Lowrider();
                break;
            case 'ComfortCruiser':
            default:
                bicycle = new ComfortCruiser();
        }
        Interface.ensureImplements(bicycle, Bicycle);
        return bicycle;
    }
}

var BicycleShop = function(){};
BicycleShop.prototype = {
    sellBicycle: function(model){
        var bicycle = BicycleFactory.createBicycle(model);
        bicycle.assemble();
        bicycle.wash();
        return bicycle;
    }
}
var shop1 = new BicycleShop();
var newBike = shop1.sellBicycle('Speedster')

简单工厂把成员对象的创建工作转交给一个外部对象。如果负责创建实例的方法的逻辑不变,那么用单体或静态类方法创建这些成员的实例是完全可以的。但是如果要提供不同品牌自行车,那么更恰当的是把这个创建方法实现在一个类中,并从中派生出一些子类。这就用到了复杂工厂函数。

🚄改成复杂工厂模式后的代码:

var BicycleShop = function() {};
BicycleShop.prototype = {
	sellBicycle: function(model) {
		var bicycle = this.createBicycle(model);
		bicycle.assemble();
		bicycle.wash();
		return bicycle;
	},
	createBicycle: function(model) {
		throw new Error('抽象类不能被实例化');
	}
};

var BicycleShop1 = function() {};
extend(BicycleShop1, BicycleShop);
BicycleShop1.prototype.createBicycle = function(model) {
	var bicycle;
	switch (model) {
		case 'Speedster':
			bicycle = new Speedster();
			break;
		case 'Lowrider':
			bicycle = new Lowrider();
			break;
		case 'ComfortCruiser':
		default:
			bicycle = new ComfortCruiser();
	}
	Interface.ensureImplements(bicycle, Bicycle);
	return bicycle;
};

var BicycleShop2 = function() {};
extend(BicycleShop2, BicycleShop);
BicycleShop2.prototype.createBicycle = function(model) {
	var bicycle;
	switch (model) {
		case 'Speedster':
			bicycle = new SpeedsterPlus();
			break;
		case 'Lowrider':
			bicycle = new LowriderPlus();
			break;
		case 'ComfortCruiser':
		default:
			bicycle = new ComfortCruiserPlus();
	}
	Interface.ensureImplements(bicycle, Bicycle);
	return bicycle;
};

复杂工厂模式不是使用另外的类或者对象创建自行车,而是使用一个子类。正式来说,工厂是一个将其成员对象的实例化推迟到子类中进行的类。本例,让各个自行车商店自行决定从哪个生产厂家进货,因此需要有各自的 createBicycle 方法。想要增加厂家,就再创建一个 BicycleShop 子类并重新定义 createBicycle 方法

特点:把一般性的代码集中在一起(此处是父类BicycleShop中),而个体性的代码被封装在子类中。

三、实例例子

🌰1. Ajax请求示例

如果代码需要多次执行 ajax 请求,那么最好是把创建请求对象的代码提取到一个类钟,并创建一个包装器来包装实际请求所需的步骤 —— 根据浏览器能力的不同来生成 XMLHttpRequest 或 ActiveXObject 实例。

var AjaxHandler = new Interface('AjaxHandler', [ 'request', 'createXhrObject' ]);

var SimpleHandler = function() {};
SimpleHandler.prototype = {
	request: function(method, url, callback, postVars) {
		var xhr = this.createXhrObject();
		xhr.onreadystatechange = function() {
			if (xhr.readyState !== 4) return;
			xhr.status === 200 ? callback.success(xhr.responseText, xhr.responseXML) : callback.failure(xhr.status);
		};
		xhr.open(method, url, true);
		if (method !== 'POST') postVars = null;
		xhr.send(postVars);
	},
	// 工厂方法
	createXhrObject: function() {
		var methods = [
			function() {
				return new XMLHttpRequest();
			},
			function() {
				return new ActiveXObject('Msxml2.XMLHttp');
			},
			function() {
				return new ActiveXObject('Microsoft.XMLHttp');
			}
		];
		for (var i = 0, len = methods.length; i < len; i++) {
			try {
				methods[i]();
			} catch (e) {
				continue;
			}
			// 如果到这一步 说明 methods[i] 已经执行过了, 那么记住这个方法
			this.createXhrObject = methods[i];
			return methods[i];
		}
		// 如果到这一步 说明上面的 methods 都没有被执行过
		throw new Error('未能创建 xhr 对象');
	}
};

createXhrObject 这个工厂方法根据当前环境返回 xhr 对象。第一次执行的时候,它会尝试三种不同方法,一旦遇到可用的方法,就会将自身变成这个方法(这种技术叫 memoizing技术)。如果第一次实现的是?XMLHttpRequest ,那么第二次执行?createXhrObject,它会是这样的 :

createXhrObject: function(){ return new XMLHttpRequest(); }

memoizing技术可以提高代码效率,因为所有设置和检测代码只执行一次。而工厂方法则是这种代码的理想封装工具。

🌰2. react.createElement 中的使用

class Vnode(tag, attrs, children){
  // ...
}
React.createElement = function(tag, attrs, children) {
    return new Vnode(tag, attrs, children)
}

// 用户使用
var profile = React.createElement("div", null, 
    React.createElement("img", {src: 'a.jpg', className: 'profile'})
);

可以看到 react?在 createElement 里封装 new Vnode,让用户直接使用?createElement 而非 vnode。这样的优点:1)省去了用户在执行 vnode 前的验证工作,交给 createElement? 处理;2)用户可以直接使用 createElement ,不需要 new 构造函数。

四、适用场合

1. 动态实现。需要创建用不同方式实现同一接口的对象,可以使用工厂模式。

2. 节省设置开销。如果对象需要复杂且彼此相关的设置,把设置代码放在类的构造函数是可以的,但是这样每次创建新实例时,这些代码都会再执行一遍。这时就可以用工厂模式,它可以在实例化所有需要对象前先一次性进行设置。

3. 用许多小型对象组成一个大对象。如果不想让某个子系统和较大的对象之间强耦合,而是在许多子系统中进行挑选,推荐选择工厂方法。

4. 不推荐场合:如果不用另外换用一个类,或者不需要在运行期间在一系列可互换的类中进行选择,就不应使用工厂方法。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-17 22:02:04  更:2022-03-17 22:03:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:31:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码