前言
HTML设计模式定义:在面向对象软件设计过程中 针对特定问题的简洁而优雅的解决方案。在不同的编程语言中,对设计模式的实现其实是可能会有区别的。
- 单例模式
- 观察者模式
- 工厂模式
- 命令模式
- 职责链模式
1,单例模式
定义:是保证一个类只有一个实例,并且提供一个访问它的全局访问点。
需求:一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的window对象、登录浮窗等。
实现:用一个变量标识当前是否已经为某个类创建过对象,如果是,则在下一次获取这个类的实例时,直接返回之前创建的对象。
优点:
- 可以用来划分命名空间,减少全局变量的数量
- 可以被实例化,且实例化一次,再次实例化生成的也是第一个实例
基础栗子:
// 单例模式var Singleton = function(name){ ???this.name = name; ???this.instance = null;};Singleton.prototype.getName = function(){ ???return this.name;};// 获取实例对象Singleton.getInstance = function(name) { ???if(!this.instance) { ???????this.instance = new Singleton(name); ???} ???return this.instance;};// 测试单例模式的实例var a = Singleton.getInstance("aa");var b = Singleton.getInstance("bb");console.log(a===b) ???// true
实践栗子:
(function () { ???//管理单例的逻辑代码,如果没有数据则创建,有数据则返回 ??var getSingle = function(fn){ //参数为创建对象的方法 ??????var result; ??????return function(){ //判断是Null或赋值 ??????????return result || (result = fn.apply(this,arguments)); ??????}; ??}; ???//创建登录窗口方法 ???var createLoginLayer = function(){ ???????var div = document.createElement(‘div‘); ???????div.innerHTML = ‘我是登录浮窗‘; ???????div.style.display = ‘none‘; ???????document.body.appendChild(div); ???????return div; ???}; ???//单例方法 ???var createSingleLoginLayer = getSingle(createLoginLayer); ???//使用惰性单例,进行创建 ???document.getElementById(‘loginBtn‘).onclick = function(){ ???????var loginLayer = createSingleLoginLayer(); ???????loginLayer.style.display = ‘block‘; ???};})()
2,观察者模式
定义:对象间的一种一对多的依赖关系。
需求:当一个对象的状态发生变化时,所有依赖于他的对象都将得到通知。
优点:时间上的解耦,对象之间的解耦。
实现:
- 首先,指定好谁充当发布者;
- 然后,给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者;
- 最后,发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数。
基础栗子:
var salesOffices = {}; ??????????????????????????// 定义售楼处salesOffices.clientList = []; ???????????????????// 缓存列表,存放订阅者的回调函数salesOffices.listen = function( fn ){ ???????????// 增加订阅者 ???this.clientList.push( fn ); ?????????????????// 订阅的消息添加进缓存列表};salesOffices.trigger = function(){ ??????????????// 发布消息 ???for( var i = 0, fn; fn = this.clientList[ i++ ]; ){ ???????fn.apply( this, arguments ); ????????????// arguments 是发布消息时带上的参数 ???}};//调用salesOffices.listen( function( price, squareMeter ){//订阅消息 ???console.log( ‘价格= ‘ + price ); ???console.log( ‘squareMeter= ‘ + squareMeter );});salesOffices.trigger( 2000000, 88 ); ???????????????// 输出:200 万,88 平方米
实践栗子:登录页面登录后,会需要刷新各个模块的信息(头像、nav)这类。
var ObserverEvent = (function () { ???????var clientList = [], listen, trigger, remove; ???????listen = function (key, fn) { ???????????if (!clientList[key]) { ???????????????clientList[key] = []; ???????????} ???????????clientList[key].push(fn); ???????}; ???????trigger = function () { ???????????var key = Array.prototype.shift.call(arguments), fns = clientList[key]; ???????????if (!fns || fns.length === 0) { ???????????????return false; ???????????} ???????????for (var i = 0, fn; fn = fns[i++];) { ???????????????fn.apply(this, arguments); ???????????} ???????}; ???????remove = function (key, fn) { ???????????var fns = clientList[key]; ???????????if (!fns) { ???????????????return false; ???????????} ???????????if (!fn) { ???????????????fns && (fns.length = 0); ???????????} else { ???????????????for (var l = fns.length - 1; l >= 0; l--) { ???????????????????var _fn = fns[l]; ???????????????????if (_fn === fn) { ???????????????????????fns.splice(l, 1); ???????????????????} ???????????????} ???????????} ???????}; ???????return { ???????????listen:listen, ???????????trigger:trigger, ???????????remove:remove ???????} ???})(); ???ObserverEvent.listen(‘squareMeter88‘, fn1 = function (price) { ???????console.log(‘价格=‘ + price); ???}); ???ObserverEvent.listen(‘squareMeter100‘, function (price) { ???????console.log(‘价格=‘ + price); ???}); ???ObserverEvent.trigger(‘squareMeter88‘, 200000);//刷新模块信息var header = (function () { ???????ObserverEvent.listen(‘loginSucc‘, function (data) { ???????????header.setAvatar(data.avatar); ???????}); ???????return { ???????????setAvatar: function (data) { ???????????????console.log(data + "设置header成功"); ???????????} ???????} ???})(); ???var nav = (function () { ???????ObserverEvent.listen(‘loginSucc‘, function (data) { ???????????nav.setAvatar(data.avatar) ???????}); ???????return { ???????????setAvatar: function (data) { ???????????????console.log(data + ‘设置nav成功‘); ???????????} ???????} ???})(); ???var data = {}; ???data.avatar = "参数"; ???ObserverEvent.trigger(‘loginSucc‘, data);
3,工厂模式:
定义:将其成员对象的实例化推迟到子类来实现的类。
需求:创建对象的流程赋值的时候,比如依赖于很多设置文件等 ;处理大量具有相同属性的小对象;注:不能滥用
优点:不暴露创建对象的具体逻辑,而是将将逻辑封装在一个函数中。
分类:简单工厂,工厂方法和抽象工厂。
实现:
3.1 简单工厂模式 (创建单一对象,需要的类比较少)
let UserFactory = function (role) { ?function SuperAdmin() { ???this.name = "超级管理员", ???this.viewPage = [‘首页‘, ‘通讯录‘, ‘发现页‘, ‘应用数据‘, ‘权限管理‘] ?} ?function Admin() { ???this.name = "管理员", ???this.viewPage = [‘首页‘, ‘通讯录‘, ‘发现页‘, ‘应用数据‘] ?} ?function NormalUser() { ???this.name = ‘普通用户‘, ???this.viewPage = [‘首页‘, ‘通讯录‘, ‘发现页‘] ?} ?switch (role) { ???case ‘superAdmin‘: ?????return new SuperAdmin(); ?????break; ???case ‘admin‘: ?????return new Admin(); ?????break; ???case ‘user‘: ?????return new NormalUser(); ?????break; ???default: ?????throw new Error(‘参数错误, 可选参数:superAdmin、admin、user‘); ?}}
3.2 工厂方法模式 (创建多类对象,需要的类比较多)
为方便后续新增类方便,只需改一处代码,封装了工厂方法而已。并且把类都放在工厂类原型中实现。
//安全模式创建的工厂方法函数let UserFactory = function(role) { ?if(this instanceof UserFactory) { ???var s = new this[role](); ???return s; ?} else { ???return new UserFactory(role); ?}}//工厂方法函数的原型中设置所有对象的构造函数UserFactory.prototype = { ?SuperAdmin: function() { ???this.name = "超级管理员", ???this.viewPage = [‘首页‘, ‘通讯录‘, ‘发现页‘, ‘应用数据‘, ‘权限管理‘] ?}, ?Admin: function() { ???this.name = "管理员", ???this.viewPage = [‘首页‘, ‘通讯录‘, ‘发现页‘, ‘应用数据‘] ?}, ?NormalUser: function() { ???this.name = ‘普通用户‘, ???this.viewPage = [‘首页‘, ‘通讯录‘, ‘发现页‘] ?}}//调用let superAdmin = UserFactory(‘SuperAdmin‘);let admin = UserFactory(‘Admin‘) let normalUser = UserFactory(‘NormalUser‘)
3.3 抽象工厂模式 (创建父类,子类继承父类,具体实现在子类)
抽象工厂其实是实现子类继承父类的方法,只是一个方法。
抽象工厂模式一般用在多人协作的超大型项目中,并且严格的要求项目以面向对象的思想进行完成。
// 抽象工厂方法var VehicleFatory = function(subType, superType) { ???// 判断抽象工厂中是否有该抽象类 ???if(typeof VehicleFactory[superType] === ‘function‘) { ???????// 缓存类 ???????function F() {}; ???????// 继承父类属性和方法 ???????F.prototype = new VehicleFactory[superType] (); ???????// 将子类constructor 指向子类 ???????subType.constructor = subType; ???????// 子类原型继承‘父类‘ ???????subType.prototype = new F(); ???} else { ???????// 不存在该抽象类抛出错误 ???????throw new Error(‘未创建该抽象类‘); ???}};// 小汽车抽象类VehicleFactory.Car = function() { ???this.type = ‘car‘;};VehicleFactory.Car.prototype = { ???getPrice: function() { return new Error(‘抽象方法不能调用‘); }, ???getSpeed: function() { return new Error(‘抽象方法不能调用‘); }};// 公交车抽象类VehicleFactory.Bus = function() { ???this.type = ‘bus‘;};VehicleFactory.Bus.prototype = { ???getPrice: function() { return new Error(‘抽象方法不能调用‘); }, ???getSpeed: function() { return new Error(‘抽象方法不能调用‘); }};// 货车抽象类VehicleFactory.Truck = function() { ???this.type = ‘truck‘;};VehicleFactory.Truck.prototype = { ???getPrice: function() { return new Error(‘抽象方法不能调用‘); }, ???getSpeed: function() { return new Error(‘抽象方法不能调用‘); }};// 创建产品子类继承相应的产品簇抽象类// 宝马汽车子类var BMW = function(price, speed) { ???this.price = price; ???this.speed = speed;}//抽象工厂实现对Car抽象类的继承VehicleFactory(BMW, ‘Car‘);BMW.prototype.getPrice = function() { return this.price };BMW.prototype.getSpeed = function() { return this.speed };// 公交车...// 货车...
4,命令模式:
定义:用来对方法调用进行参数化处理和传送,经过这样处理过的方法调用可以在任何需要的时候执行。
需求:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么,此时希望用一种松耦合的方式来设计软件,使得请求发送者和请求接收者能够消除彼此之间的耦合关系。
实现:将函数的调用、请求和操作封装成一个单一的对象。
1 var setCommand = function(button,func) { 2 ????button.onclick = function(){ 3 ????????func(); 4 ????} 5 ?}; ?6 ?var MenuBar = { 7 ????refersh: function(){ 8 ????????alert("刷新菜单界面"); 9 ????}10 ?};11 ?var SubMenu = {12 ????add: function(){13 ????????alert("增加菜单");14 ????}15 ?};16 ?// 刷新菜单17 ?var RefreshMenuBarCommand = function(receiver) {18 ????return function(){19 ????????receiver.refersh(); ???20 ????};21 ?};22 ?// 增加菜单23 ?var AddSubMenuCommand = function(receiver) {24 ????return function(){25 ????????receiver.add(); ???26 ????};27 ?};28 ?var refershMenuBarCommand = RefreshMenuBarCommand(MenuBar);29 ?// 增加菜单30 ?var addSubMenuCommand = AddSubMenuCommand(SubMenu);31 ?setCommand(b1,refershMenuBarCommand);32 33 ?setCommand(b2,addSubMenuCommand);
5,职责链模式:
定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。(大函数分割成一个个小函数,清晰,各司其职)
需求:代码不清晰,可读性差,拆分函数。
实现:
//----------------------改造前---------------
var order = function( orderType, pay, stock ){if ( orderType === 1 ){ // 500 元定金购买模式 ???if ( pay === true ){ // 已支付定金 ???????console.log( ‘500 元定金预购, 得到 100 优惠券‘ ); ???}else{ // 未支付定金,降级到普通购买模式 ???????if ( stock > 0 ){ // 用于普通购买的手机还有库存 ???????????console.log( ‘普通购买, 无优惠券‘ ); ???????????}else{ ???????????????console.log( ‘手机库存不足‘ ); ???????????????} ???????????} ???????} ???????else if ( orderType === 2 ){ // 200 元定金购买模式 ???????????if ( pay === true ){ ???????????????console.log( ‘200 元定金预购, 得到 50 优惠券‘ ); ???????????}else{ ???????????????if ( stock > 0 ){ ???????????????????console.log( ‘普通购买, 无优惠券‘ ); ???????????????}else{ ???????????????????console.log( ‘手机库存不足‘ ); ???????????????} ???????????} ???????} ??????else if ( orderType === 3 ){ ???????????if ( stock > 0 ){ ???????????????console.log( ‘普通购买, 无优惠券‘ ); ???????????}else{ ???????????????console.log( ‘手机库存不足‘ ); ???????????} ???????} ???}; ???order( 1 , true, 500); // 输出: 500 元定金预购, 得到 100 优惠券
//--------------------- 改造后----------------------------
// 500 元订单var order500 = function( orderType, pay, stock ){ ???if ( orderType === 1 && pay === true ){ ???????console.log( ‘500 元定金预购, 得到 100 优惠券‘ ); ???}else{ ???????order200( orderType, pay, stock ); // 将请求传递给 200 元订单 ???}};// 200 元订单var order200 = function( orderType, pay, stock ){ ???if ( orderType === 2 && pay === true ){ ???????console.log( ‘200 元定金预购, 得到 50 优惠券‘ ); ???}else{ ???????orderNormal( orderType, pay, stock ); // 将请求传递给普通订单 ???}};// 普通购买订单var orderNormal = function( orderType, pay, stock ){ ???if ( stock > 0 ){ ???????console.log( ‘普通购买, 无优惠券‘ ); ???}else{ ???????console.log( ‘手机库存不足‘ ); ???}};// 测试结果:order500( 1 , true, 500); // 输出:500 元定金预购, 得到 100 优惠券order500( 1, false, 500 ); // 输出:普通购买, 无优惠券order500( 2, true, 500 ); // 输出:200 元定金预购, 得到 500 优惠券order500( 3, false, 500 ); // 输出:普通购买, 无优惠券order500( 3, false, 0 ); // 输出:手机库存不足
HTML前端各种常见的设计模式
原文地址:https://www.cnblogs.com/xiaozhu520/p/9936796.html