什么是设计模式
设计模式是对一些常见问题进行归纳总结,并针对具体问题给出一套通用的解决办法(强调的是解决问题的思想);在开发中,只要遇到这类问题,就可以直接使用这些设计模式解决问题;最早起源于建筑领域,在建筑领域把一些问题和经验进行归纳总结,形成一套可以用来在建筑领域解决大多数问题的方案;后来计算机领域借鉴了建筑领域的设计模式,把计算机领域中经常遇到的问题进行归纳和总结,形成计算机领域23种设计模式。
传统单例模式
通俗举例来说,单例模式就相当于一些软件或者网站的登录窗口,点击登录,然后取消再次点击登录,弹出的登录窗口依然是那一个,窗口不会被重新创建。
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
实现单例核心思想
是用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象
用js代码实现单例模式
var CreateDiv = function (text) { //构造函数,初始化一个对象(对象又称为实例)
this.text = text;
this.init();
};
CreateDiv.prototype.init = function () {
var div = document.createElement('div');
div.innerHTML = this.text;
document.body.appendChild(div);
};
var ProxySingletonCreateDiv = (function () { //实现单例的函数
var instance;
return function (text) {
if (!instance) { //若instance实例不存在,则创建
instance = new CreateDiv(html);
}
return instance;
}
})();
var a = new ProxySingletonCreateDiv('sven1');
var b = new ProxySingletonCreateDiv('sven2');
alert(a === b); //true
工厂模式
通俗举例来说就相当于我们见到的路边自动贩卖机,作为消费者我们只需用选择商品和付款就可以得到我们想要的,而自动贩卖机是怎么识别选择、怎么工作的,我们是不需用知道的。自动贩卖机会隐藏它工作原理和机制,它就相当于一个工厂。
工厂模式是一种用来创建对象的设计模式。我们不暴露对象创建的逻辑,而是将逻辑封装在一个函数内,那么这个函数可以成为工厂。
工厂模式根据抽象程度的不同可以分为:
-
简单工厂
-
工厂方法
-
抽象工厂
简单工厂模式
用js代码实现简单工厂模式
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');
factory就是一个简单的工厂,该工厂中有一个构造函数分别对应不同的权限。我们只需要传递相应的参数就可以获取一个实例对象了
简单工厂的优点:
只需要传递一个合法的参数,就可以获取到想要的对象,而无需知道创建的具体的细节。
但是在函数内包含了所有对象的构造函数和判断逻辑的代码, 每次如果需要添加一个对象,那么我们需要新增一个构造函数,当我们需要维护的对象不是上面这2个, 而是20个或者更多,那么这个函数将会成为超级函数,使得我们难以维护。
所以简单工厂模式只适用于在创建时对象数量少,以及逻辑简单的情况。
工厂方法
工厂方法模式本意是将实际创造的对象推迟到子类中,这样核心类就变成了抽象类。但是在js中很难像那些传统面向对象语言那样去实现抽象类,所以在js中我们只需要参考他的思想即可。
我们可以把工厂函数看成是一个工厂类。在简单模式,我们添加一个新的对象需要修改两处地方,在加入工厂方法模式以后,我们只需要修改一处即可。工厂方法的工厂类,他只做实例化这一件事情。我们只需要修改他的原型类即可。
我们采用安全模式创建工厂对象。
用js代码实现工厂方法
let factory = function (role) {
if(this instanceof factory) { //instanceof是一个运算符,用来判断一个对象是否是某个类的实例
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');
抽象工厂模式
(1)抽象工厂模式提供了一种封装一组具有相同主题的单个工厂而无需指定其具体类的方法。即工厂的工厂; 一个将单个但相关/从属工厂分组在一起的工厂,但未指定其具体类别。
(2)一个抽象工厂创建了由一个共同主题相关的对象。在面向对象的编程中,工厂是一个创建其他对象的对象。 一个抽象工厂已经抽象出了一个主题,这个主题被新创建的对象所共享。
(3)你可能会奇怪为什么要把构造对象的责任交给别人,而不是直接用new关键字调用构造函数。
原因是:构造函数对整个创建过程的控制是有限的, 有时你需要把控制权交给一个拥有更广泛知识的工厂。这包括创建过程中涉及对象缓存、对象共享或重用、复杂逻辑、或维护对象和类型计数的应用程序、以及与不同资源或设备交互的对象等场景。如果您的应用程序需要对对象创建过程进行更多控制,请考虑使用抽象工厂模式。
抽象工厂模式作用
当存在相互关联的依赖关系且涉及非简单创建逻辑时,建议使用抽象工厂模式。通常在以下情况下考虑使用抽象工厂模式:
-
一个系统应该独立于其产品的创建,组成和表示方式
-
系统应配置有多个产品系列之一
-
一个相关产品对象系列只在一起使用,因此需要强制执行此约束
-
想要提供产品的类库,并且只想显示它们的接口,而不是它们的实现
-
从概念上讲,依赖项的生存期短于消费者的生存期。
-
需要一个运行时值来构建特定的依赖关系
-
想决定在运行时从系列中调用哪种产品。
-
需要提供一个或多个仅在运行时才知道的参数,然后才能解决依赖关系。
-
当需要产品之间的一致性时
-
在将新产品或产品系列添加到程序时,您不想更改现有代码。
用js代码实现抽象工厂模式
//员工类
function Employee(name) {
this.name = name;
this.say = function() {
log.add("我是员工:" + name);
};
}
//员工工厂
function EmployeeFactory() {
this.create = function(name) {
return new Employee(name);
};
}
//供应商类
function Vendor(name) {
this.name = name;
this.say = function() {
log.add("我是供应商:" + name);
};
}
//供应商工厂
function VendorFactory() {
this.create = function(name) {
return new Vendor(name);
};
}
// 日志函数
var log = (function() {
var log = "";
return {
add: function(msg) {
log += msg + "\n";
},
show: function() {
console.info("%c%s", "color: red; background: #b4b3b3; font-size: 20px", log);
log = "";
}
}
})();
function run() {
var persons = [];
var employeeFactory = new EmployeeFactory();
var vendorFactory = new VendorFactory();
persons.push(employeeFactory.create("张三"));
persons.push(employeeFactory.create("李四"));
persons.push(vendorFactory.create("王麻子"));
persons.push(vendorFactory.create("赵六"));
for (var i = 0, len = persons.length; i < len; i++) {
persons[i].say();
}
log.show();
}