前端常用的设计模式

什么是设计模式?
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。针对特定问题制定的简洁而优雅的解决方案。

单例模式、观察者模式(发布-订阅模式)、工厂模式、命令模式、职责链模式、模块模式、代理模式(暂时没写)

1、单例模式
定义:保证一个类只有一个实力,并且提供一个访问它的全局访问点。
需求:一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的window对象、登录浮窗等。
实现:用一个变量标识当前是否已经为某个类创建过对象,如果是,则在下一次获取这个类的实例时,直接返回之前创建的对象。
优点:可以用来划分命名空间,减少全局变量的数量;可以被实例化,且实例化一次,再次实例化生成的也是第一个实例

// 单例模式
var lemon  = function(name) {
	this.name = name;
	this.instance = null;
};
lemon.prototype.getName = function() {
	return this.name;
}
// 获取实例对象
lemon.getInstance = function(name) {
	if(!this.instance) {
		this.instance = new lemon(name);
	}
	return this.instance;
}
// 测试单例模式的实例
var a = lemon.getInstance('aa');
var b = lemon.getInstance('bb');

console.log(a === b)             // true;

2、观察者模式
定义:对象间的一种一对多的依赖关系。
需求:当一个对象的状态发生变化时,所有依赖于他的对象都将得到通知。
优点:时间上的解耦,对象之间的解耦。
应用:DOM事件是一种典型的发布-订阅模式,对一个dom节点的一个事件进行监听,当操作dom节点时,触发相应的事件,响应函数执行。事件函数对dom节点完全未知,不用去理会是什么事件,如何触发,执行就好

  • 发布-订阅模式广泛应用于异步编程之中,是一种替代回调函数的方案.多个事件处理函数可以订阅同一个事件,当该事件发生后,与其相对应的多个事件处理函数都会运行
  • 取代对象之间硬编码的通知机制,一个对象不用再显示的调用另外一个对象的某个接口,降低模块之间的耦合程度,虽然不清楚彼此的细节,但是不影响他们之间相互通信
var salesOffices = {};    // 定义售楼处
salesOffices.clientList = [];     // 缓存列表,存放订阅者的回调函数
salesOffices.listen = function(fn) {    // 增加订阅者
	this.clientList.push(fn);       // 订阅的消息添加进缓存列表
};
slaesOffices.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(20000, 88);       //  输出:200 万,88 平方米

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,命令模式:
定义:用来对方法调用进行参数化处理和传送,经过这样处理过的方法调用可以在任何需要的时候执行。
需求:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么,此时希望用一种松耦合的方式来设计软件,使得请求发送者和请求接收者能够消除彼此之间的耦合关系。
实现:将函数的调用、请求和操作封装成一个单一的对象。

var setCommand = function(button,func) {
    button.onclick = function(){
        func();
    }
 }; 
 var MenuBar = {
    refersh: function(){
        alert("刷新菜单界面");
    }
 };
 var SubMenu = {
    add: function(){
        alert("增加菜单");
    }
 };
 // 刷新菜单
 var RefreshMenuBarCommand = function(receiver) {
    return function(){
        receiver.refersh();    
    };
 };
 // 增加菜单
 var AddSubMenuCommand = function(receiver) {
    return function(){
        receiver.add();    
    };
 };
 var refershMenuBarCommand = RefreshMenuBarCommand(MenuBar);
 // 增加菜单
 var addSubMenuCommand = AddSubMenuCommand(SubMenu);
 setCommand(b1,refershMenuBarCommand);

 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 ); // 输出:手机库存不足

7、模块模式
在立即执行函数表达式中定义的变量和方法在外界是访问不到的,只能通过其向外部提供的接,"有限制"地访问。通过函数作用域解决了属性和方法的封装问题。

var person = (function() {
	var name = 'zcc';
	var age = 25;
	function getName() {
		return name;
	}
	function getAge() {
		return age;
	}
	return {
		getName: getName,
		getAge: getAge
	}
})();

console.log(name)  // error
console.log(age);   // error
console.log(person.name);    // undefined
console.log(person.age);    // undefined
console.log(person.getName());   // zcc
console.log(person.getAge());   // 25

8、代理模式
参考:https://www.cnblogs.com/shamoguying1140/p/3169206.html

原文链接: https://www.cnblogs.com/smlp/p/9776789.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值