设计模式分类(23种设计模式)
- 创建型
- 单例模式
- 原型模式
- 工厂模式
- 抽象工厂模式
- 建造者模式
- 结构型
- 适配器模式
- 装饰器模式
- 代理模式
- 外观模式
- 桥接模式
- 组合模式
- 享元模式
- 行为型
- 观察者模式
- 迭代器模式
- 策略模式
- 模板方法模式
- 职责链模式
- 命令模式
- 备忘录模式
- 状态模式
- 访问者模式
- 中介者模式
- 解释器模式
工厂模式
什么是工厂模式?一个工厂接到一笔订单(传参),然后根据这个订单类型(参数)来安排产品线(实例化哪个类),当然客户可以要求一些产品的工艺属性(抽象工厂)。这其中厂长(工厂模式)只负责调度,即安排产品零件流水线。你应该知道的是,工厂有个特点就是产出体量大、相似度高的产品。如果你要做单一定制化的产品,那这笔订单给工厂就不适用了。
举个例子:
- 编程中,在一个 A 类中通过 new 的方式实例化了类 B,那么 A 类和 B 类之间就存在关联(耦合);
- 后期因为需要修改了 B 类的代码和使用方式,比如构造函数中传入参数,那么 A 类也要跟着修改,一个类的依赖可能影响不大,但若有多个类依赖了 B 类,那么这个工作量将会相当的大,容易出现修改错误,也会产生很多的重复代码,这无疑是件非常痛苦的事;
- 这种情况下,就需要将创建实例的工作从调用方(A类)中分离,与调用方解耦,也就是使用工厂方法创建实例的工作封装起来(减少代码重复),由工厂管理对象的创建逻辑,调用方不需要知道具体的创建过程,只管使用,而降低调用者因为创建逻辑导致的错误;
使用场景
- 处理大量具有相同属性的小对象;
- 对象的构建十分复杂,需要依赖具体环境创建不同实例;
工厂模式的三种实现方法: 简单工厂模式
、工厂方法模式
、抽象工厂模式
。
1. 简单工厂模式
简单工厂模式
又叫静态工厂模式
,由一个工厂对象决定创建某一种产品对象类的实例。主要用来创建同一类对象。
//User类
class User {
//构造器
constructor(opt) {
this.name = opt.name;
this.viewPage = opt.viewPage;
}
//静态方法
static getInstance(role) {
switch (role) {
case 'superAdmin':
return new User({ name: '超级管理员', viewPage: ['首页', '通讯录', '发现页', '应用数据', '权限管理'] });
break;
case 'admin':
return new User({ name: '管理员', viewPage: ['首页', '通讯录', '发现页', '应用数据'] });
break;
case 'user':
return new User({ name: '普通用户', viewPage: ['首页', '通讯录', '发现页'] });
break;
default:
throw new Error('参数错误, 可选参数:superAdmin、admin、user')
}
}
}
//调用
let superAdmin = User.getInstance('superAdmin');
let admin = User.getInstance('admin');
let normalUser = User.getInstance('user');
User就是一个简单工厂,在该函数中有3个实例中分别对应不同的权限的用户。当我们调用工厂函数时,只需要传递superAdmin, admin, user这三个可选参数中的一个获取对应的实例对象。
简单工厂的优点在于,你只需要一个正确的参数,就可以获取到你所需要的对象,而无需知道其创建的具体细节。但是在函数内包含了所有对象的创建逻辑(构造函数)和判断逻辑的代码,每增加新的构造函数还需要修改判断逻辑代码。当我们的对象不是上面的3个而是30个或更多时,这个函数会成为一个庞大的超级函数,便得难以维护。所以,简单工厂只能作用于创建的对象数量较少,对象的创建逻辑不复杂时使用。
2. 工厂方法模式
工厂方法模式的本意是将实际创建对象的工作推迟到子类中,这样核心类就变成了抽象类。这样添加新的类时就无需修改工厂方法,只需要将子类注册进工厂方法的原型对象中即可。
class User {
constructor(name = '', viewPage = []) {
if(new.target === User) {
throw new Error('抽象类不能实例化!');
}
this.name = name;
this.viewPage = viewPage;
}
}
class UserFactory extends User {
constructor(name, viewPage) {
super(name, viewPage)
}
create(role) {
switch (role) {
case 'superAdmin':
return new UserFactory( '超级管理员', ['首页', '通讯录', '发现页', '应用数据', '权限管理'] );
break;
case 'admin':
return new UserFactory( '普通用户', ['首页', '通讯录', '发现页'] );
break;
case 'user':
return new UserFactory( '普通用户', ['首页', '通讯录', '发现页'] );
break;
default:
throw new Error('参数错误, 可选参数:superAdmin、admin、user')
}
}
}
let userFactory = new UserFactory();
let superAdmin = userFactory.create('superAdmin');
let admin = userFactory.create('admin');
let user = userFactory.create('user');
3. 工厂方法模式
抽象工厂只留对外的口子,不做事,留给外界覆盖(子类重写接口方法以便创建的时候指定自己的对象类型)。主要用于对产品类簇的创建,不直接生成实例(简单工厂模式和工厂方法模式都是生成实例)。
- 抽象类是一种声明但不能使用的类,子类必须先实现其方法才能调用;
- 可以在抽象类中定义一套规范,供子类去继承实现;
// 抽象工厂
function AbstractFactory(subType, superType) {
if (typeof AbstractFactory[superType] === 'function') {
//缓存类
function F() { }
//继承父类属性和方法
F.prototype = new AbstractFactory[superType]();
//将子类 constructor 指向子类(自己)
subType.prototype.constructor = subType;
//子类原型继承缓存类(父类)
subType.prototype = new F();
} else {
//不存在该抽象类抛出错误
throw new Error('抽象类不存在')
}
}
// 抽象类
AbstractFactory.Phone = function () {
this.type = 'Phone';
}
AbstractFactory.Phone.prototype = {
showType: function () {
return new Error('Phone 抽象方法 showType 不能调用');
},
showPrice: function () {
return new Error('Phone 抽象方法 showPrice 不能调用');
},
showColor: function () {
return new Error('Phone 抽象方法 showColor 不能调用');
}
}
AbstractFactory.Pad = function () {
this.type = 'Pad';
}
AbstractFactory.Pad.prototype = {
showType: function () {
return new Error('Pad 抽象方法 showType 不能调用');
},
showPrice: function () {
return new Error('Pad 抽象方法 showPrice 不能调用');
},
showColor: function () {
return new Error('Pad 抽象方法 showColor 不能调用');
}
}
// 抽象工厂实现对抽象类的继承
function Iphone(type, price, color) {
this.type = type;
this.price = price;
this.color = color;
}
//抽象工厂实现对 Phone 抽象类的继承
AbstractFactory(Iphone, 'Phone');
Iphone.prototype.showType = function () {
return this.type;
}
Iphone.prototype.showPrice = function () {
return this.price;
}
Iphone.prototype.showColor = function () {
return this.color;
}
function Ipad(type, price, color) {
this.type = type;
this.price = price;
this.color = color;
}
AbstractFactory(Ipad, 'Pad');
Ipad.prototype.showType = function () {
return this.type;
}
Ipad.prototype.showPrice = function () {
return this.price;
}
Ipad.prototype.showColor = function () {
return this.color;
}
// 实例
var iphone5s = new Iphone('iphone 5s', 3000, '白色');
console.log('今天刚买了' + iphone5s.showType() + ',价格是' + iphone5s.showPrice() + ',' + iphone5s.showColor())
var iphone8s = new Iphone('iphone 8s', 8000, '白色');
console.log('今天刚买了' + iphone8s.showType() + ',价格是' + iphone8s.showPrice() + ',' + iphone8s.showColor())
var ipad = new Ipad('ipad air', 2000, '骚红色');
console.log('今天刚买了' + ipad.showType() + ',价格是' + ipad.showPrice() + ',' + ipad.showColor())
单例模式
一个类只有一个实例,并提供一个访问它的全局访问点。
详细可参考:https://segmentfault.com/a/1190000022831974
- 利用闭包实现单例模式
function Singleton(name) {
this.name = name
}
var proxySingle = (function(){
var instance
return function(name) {
if(!instance) {
instance = new Singleton(name)
}
return instance
}
})()
适配器模式
将一个类(对象)的接口(方法或属性)转化成客户希望的另外一个接口(方法或属性),使得原本由于接口不兼容而不能一起工作的那些类(对象)可以正常协作。简单理解就是为兼容而生的 “转换器”。
class Plug {
getName() {
return 'iphone充电头';
}
}
class Target {
constructor() {
this.plug = new Plug();
}
getName() {
return this.plug.getName() + ' 适配器Type-c充电头';
}
}
let target = new Target();
target.getName(); // iphone充电头 适配器转Type-c充电头
观察者模式
定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使它们能够自动更新自己,当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。
// 主题 保存状态,状态变化之后触发所有观察者对象
class Subject {
constructor() {
this.state = 0
this.observers = []
}
getState() {
return this.state
}
setState(state) {
this.state = state
this.notifyAllObservers()
}
notifyAllObservers() {
this.observers.forEach(observer => {
observer.update()
})
}
attach(observer) {
this.observers.push(observer)
}
}
// 观察者
class Observer {
constructor(name, subject) {
this.name = name
this.subject = subject
this.subject.attach(this)
}
update() {
console.log(`${this.name} update, state: ${this.subject.getState()}`)
}
}
// 测试
let s = new Subject()
let o1 = new Observer('o1', s)
let o2 = new Observer('02', s)
s.setState(12)