工厂模式: 用来创建对象的创建型模式,在上一章的ajax封装中,我们利用简单工厂创建了ajax框架,而不用去关注内部实现。这也是工厂模式的好处之一。通过使用工厂方法而不是new关键字及具体类,你可以把所有实例化代码集中在一个位置,可以大大简化更换所用的类或在运行期间动态选择所用的类的工作。
应用场景在上一篇文章给过, 在创建ajax对象时,我们不关注ajax 内部的对象如何实现,通过判断浏览器的类型,case到不同的ajax对象,达到实例集中管理,并不关注内部实现的目的。
以下是简单的工厂的实现代码。
// 工厂方法
let factory = function (role) {
function User(obj) {
this.name = obj.name;
this.role = obj.role;
}
switch(role) {
case 'superman':
return new User({ name: '平台用户', role: ['主页', '登录页'] })
break;
case 'man':
return new User({ name: '游客', role: ['登录页']})
break;
default:
throw new Error('参数错误')
}
}
// 工厂方法创建不同的实例
let superman = factory('superman');
let man = factory('man');
// 简单工厂的弊端会导致如果需要更多实例需要维护一个超级函数
var Dialog = (function(){
var createNotice = function(){
return '<div>notice</div>';
}
var createToast = function(){
return '<div>toast</div>';
}
var createWarnin = function(){
return '<div>warnin</div>';
}
var Dialog = function(){
this.element = '';
this.name = '';
this.show = function(){
console.log(this.name + ' is show -> ' + this.element);
};
}
return {
factory: function(arg){
var _dialog;
if(arg === 'notice'){
_dialog = new Dialog();
_dialog.element = createNotice();
_dialog.name = 'notice';
}else if(arg === 'toast'){
_dialog = new Dialog();
_dialog.element = createToast();
_dialog.name = 'toast';
}else if(arg === 'warnin'){
_dialog = new Dialog();
_dialog.element = createWarnin();
_dialog.name = 'warnin';
}
return _dialog;
}
}
})();
var notice = Dialog.factory('notice');
var toast = Dialog.factory('toast');
var warnin = Dialog.factory('warnin');
toast.show(); //toast is show -> <div>toast</div>
notice.show(); //notice is show -> <div>notice</div>
warnin.show(); //warnin is show -> <div>warnin</div>
简单工厂的适用场景: 简单、 可穷举的工厂实例。因为工厂的方法足够简单,而一个大型项目中往往需要我们进行大批量的拓展,此时简单工厂已经不符合软件的开闭原则,我们并不是每次都要改实现。
开闭原则:对扩展开放,对修改源码行为关闭。
工厂方法: 是在简单工厂上的再拓展,通过new关键字进行实例的创建,此时在创建时遍会再次执行构造函数 对构造的实例进行初始化。
// 创建一个工厂方法来返回不同的方法
let factory = function (role) {
if(this instanceof factory) {
var s = new this[role]();
return s;
} else {
return new factory(role);
}
}
// 下一次新增新的特性时只需要在函数上绑定数据即可
factory.prototype = {
admin: function() {
this.name = '平台用户';
this.role = ['登录页', '主页']
},
common: function() {
this.name = '游客';
this.role = ['登录页']
},
test: function() {
this.name = '测试';
this.role = ['登录页', '主页', '测试页'];
this.test = '我还有一个测试属性哦'
}
}
// 工厂方法创建
let admin = new factory('admin');
let common = new factory('common');
let test = new factory('test');
此时当我们需要给角色增加新特性,或者增加新角色时,只需要在原型上增加新的角色or 新的方法即可,此时即达到了工厂方法的扩展。
江湖传言, 没有什么是不能通过增加一个抽象层解决的。所以抽象工厂更像是一个供应链,或者说是一个提供一个大而全功能的百货公司,通过不同需求提供不同工厂产出的货物。
以下是抽象工厂的代码实现 代码源自知乎,https://zhuanlan.zhihu.com/p/55840258
abstract class Department {
// 初始化name成员,参数属性
constructor(public name: string){
}
printName(): void{
console.log('Department name: ' + this.name);
}
abstract printMeeting(): void; // 必须在派生类中实现
}
class AccountingDepartment extends Department {
constructor() {
super('Accounting and Auditing'); // 在派生类的构造函数中必须调用super()
}
printMeeting(): void{
console.log('The Accounting Department meets each Monday at 10am.');
}
generateReports(): void{
console.log('Generating accounting reports...')
}
}
let department: Department; // 允许创建一个对抽象类型的引用
department = new Department(); // 错误:不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
department.generateReports(); // 错误:方法在声明的抽象类种不存在
抽象类像是一个百货公司,定义了公司是发展方向,提供的东西,而真正提供供货货源的人可能来自京东 淘宝 拼多多,这是我对抽象工厂的理解。
在实际应用场景中,你是否有使用抽象工厂,工厂方法, 简单工厂呢?
/*
* @lc app=leetcode.cn id=1 lang=javascript
*
* [1] 两数之和
*/
// @lc code=start
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function (nums, target) {
// 设置容器对象
var map = {};
// 遍历数组
for (let index = 0; index < nums.length; index++) {
// 当数组中存在
if (map[target - nums[index]] >= 0) {
return [map[target - nums[index]], index];
}
map[nums[index]] = index;
}
};
此处使用在对象中找看是否找得到的方式来锁定了数组的下标,采用这种查找的方式在很多求索引的题目中经常会遇到,本次也是一个比较经典的案例。你还有其他的解法吗,欢迎在下方留言哟。
给定一个排序的整数数组(升序)和一个要查找的整数target,用O(logn)的时间查找到target第一次出现的下标(从0开始),如果target不存在于数组中,返回-1。