子慕谈设计模式系列(三)

前言

设计模式不容易用文字描述清楚,而过多的代码,看起来也让人摸不到头脑,加上词语或者文字描述的抽象感,很容易让人看了无数设计模式的文章,也仍然理解不了。  所以我一直打算写此系列博客,首先我会从大量文章里去理解这些设计模式,最后我用自己的语言组织转化为博客,希望用更少的代码,更容易理解的文字,来聊一聊这些设计模式。  我所理解、所描述的每一个设计模式也可能有些是错误的,甚至也不一定有非常深刻的理解,所以希望有人指出,我可以更改博客内容。  我作为前端开发者,所以设计模式的代码以前端代码和视角为主。  此博客内容对每一种模式并不会写得非常深入,也许能为读者打通一些认知,如果看了此系列博客,再去看其他更深入的博客,可能是一种比较好的方式。

 

代理模式

代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

生活中的例子:买火车票不一定在火车站买,也可以去代售点或者网上购买,vpn。

常见的代理模式应用例子:前后端交互,往往后端有server层和应用层,前端只和后端应用层打交道,应用层调取server层,server层职责单一,应用层处理逻辑。  应用层既是代理层,也可以叫中间件。

代理模式最开始我一直没有想到平时前端应用中的例子。但是经过多次推敲和联系,发现代理模式的使用无处不在,再读一遍描述(在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。)。那么我认为对API的封装可以算是代理模式的应用,一般对API进行封装,肯定是希望在调用API的时候做一些通用的事情或者处理,以便让对象的调用更方便或者更适应业务。比如我们要封装一下js的location.href的跳转。我们想在跳转之前进行一些统计,那么我们可以在location.href前处理跳转逻辑。但是因为要跳转的地方太多了,我不想每个地方都有跳转逻辑,所以我封装一个jump方法,专门处理跳转,并在跳转之前处理统计逻辑。而后我在跳转前还需要做其他事情,就可以直接在这个jump方法里实现,扩展性也非常高。

function jump(url){
	//统计逻辑
	location.href = url;
}
jump('baidu.com')

优点:

  • 职责清晰
  • 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
  • 高扩展性

缺点:

  • 会使程序处理和响应速度变慢

 

中介者模式

中介者模式是用来降低多个对象和类之间的交互复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的交互,并支持松耦合,使代码易于维护。  中介者模式属于行为型模式。

用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。  对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,形成网状结构。

典型应用例子:MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。

相信现在的框架模式至少都是mvc或者mvvm。我曾经看过一个运行的老项目.net代码,一个aspx文件中写了html,css,js,.net,并且数据库查询操作也写到页面中了,那页面维护起来就非常酸爽了。  如果使用mvc模式,控制器作为中介,隔离model和view让它们不直接交互,达到了解耦效果,虽然会增加代码量,但是交互的可重用性得到了提升,代码复杂度降低,逻辑更加清晰,更易于维护,也就是所谓高内聚低耦合。

现实中的例子:在学校下发一个通知的时候,如果是通过口口相传的方式,那么学生之间的关系是多对多的关系,非常难以准确传达到每个人手里。如果学校贴一张公告单到校门口,那么公告单作为学校和学生的中介,就能让关系变得简单。我们平时生活中的聊天群也可以达到同样效果。网状结构的关系由此变成了星型结构。如下盗图:

 

 

观察者模式

观察者模式(有时又被称为发布-订阅模式)是设计模式的一种。  在此种模式中, 一个或者多个观察者去监听主题,当主题发生变化的时候,主题会通知所有的观察者。  这通常透过调用观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。  比如常用的事件监听,onclick,onload等。 

vue采用数据劫持结合观察者模式,通过Object.defineProperty方法来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的回调。  写一个删减版的类似例子(直接复制代码在控制台运行试试):

function Observer(data){//传入观察对象
    this.data = data;
    this.defineReactive(data);
}

Observer.prototype = {
    defineReactive: function(data){
        var self = this;
        var dep = new Dep();
        dep.addSub(data);//添加订阅

        Object.keys(data).forEach(function(key){
            var val = data[key];
            Object.defineProperty(data, key, {
                get: function(){
                    return val;
                },
                set: function(newVal){
                    if(val == newVal) return;
                    val = newVal;

                    dep.notify();//发生改变下发通知
                }
            })
        })
    }
}

function Dep(){}
Dep.prototype = {
    addSub: function(sub){
        this.sub = sub;
    },
    notify: function(){
        console.log(this.sub);//当前对象有值的改变,打印出当前对象
    }
}

var data = {
    val: 1
}
new Observer(data);
data.val = 2;//执行后,在控制台会打印出当前对象,直接复制这段代码运行试试

 

策略模式

  • 定义了一系列算法;
  • 封装了每个算法;
  • 这一系列的算法可互换代替。
策略模式是指对一系列的算法定义,并将每一个算法封装起来,而且使它们还可以相互替换,让算法仅仅只做算法,让其它逻辑的事情交给策略类去处理。 下面直接用例子说明。
我们常会封装一个验证工具类,一般的写法是定义一个对象字面量,不同的属性定义不同的验证方法,大致如下代码:
var validator = {
    isNumber:function(val){
        if(false) alert('非法数字');
        //return true or false
    },
    isPhone:function(){}
    //.....  
}

 这样在使用和维护验证的时候,重复代码量会比较多,一个验证方法需要处理验证算法和验证后的逻辑(验证返回、提醒等),不管提醒是写在验证方法内还是在外部方法外,这些逻辑每次都要处理一次。  那么使用策略类,来统一处理验证逻辑,并把验证算法独立开,这样之后维护只修改和添加算法方法。  那么怎么做呢,如下代码:

var validator = {
    validate: function(type, val){
        for (prop in this.types) {
            if(type == prop){//调用对应验证方法
                var result = this.types[type].validate(val);
                if(!result) alert(this.types[type].text);
                return result;
            }
        }
    },

    types: {//定义验证算法
        isNumber:{
            validate: function(val){
                //判断
                return true or false;
            },
            text: '非法数字'
        },
        isPhone:function(){}
        //.....  
    }
}

var input1 = $('input').val();
validator.validate('isNumber', input1)

以上代码只是一个小demo,并不完善,想表达的意思就是策略模式,把算法和策略类独立开来,根据需求算法可以替换,策略类统一处理。  angular的验证类FormGroup就使用了这一模式。  上面的demo因为还比较简单,所以一下感觉不到有多大差别,但是如果你真正使用过类型angular的FormGroup这类的验证方式,你就会发现能节省很多代码量,逻辑也非常清晰。

 

状态模式

允许一个对象在其内部状态改变时改变它的行为。  对象看起来似乎修改了它的类。通俗的说,对象内部定义了不同状态的类,在状态改变的时候,替换对象相应的类。

举一个生活中的例子:当我们的手机电量比较充足的时候,手机会使用正常模式用电,当电量小于20%的时候,手机会使用省电模式。电量就是状态,正常模式和省电模式就是相应的替换类。

再举一个前端的例子: 博客园的后台首页右上角,有两种形态,一种是登录之后,会显示当前用户名和注销按钮,一种是没有登录会展示登录按钮和注册按钮。  假如博客园使用了双向绑定, 那么在模板里,会有登录和未登录的html代码,然后通过一个指令(比如ngShow)传入控制器变量操作谁隐藏谁显示。  那么控制器的变量就是状态,登录和未登录模板代码就是状态对应的类。  这个模式我想作为前端肯定是会常常使用的。所以此模式就不用代码描述了。

 

此系列博客目录:

 

子慕谈设计模式系列(一)

子慕谈设计模式系列(二)——设计模式六大原则

子慕谈设计模式系列(三)

posted @ 2017-10-16 12:31  子慕大诗人  阅读(988)  评论(4编辑  收藏  举报