JavaScript设计模式

你将在本文中,了解什么是设计模式!

你将了解设计模式有什么优点!

你将了解两种设计模式-观察者模式和装饰模式!

一、什么是设计模式

  什么是设计模式?其实简单可以理解为,设计模式其实是一套理论,它可以提高代码的可重用性,增强系统的可维护性,以及解决一系列的复杂问题。模式的另一种解释就是一个我们如何解决问题的模版,提供解决问题的一种方案。

二、设计模式的优点

模式是行之有效的一种解决方法:通过长期以来前辈们提供固定的解决方案在软件开发中出现的问题。

模式是可以很容易地重用:一个模式通常反映解决一些问题可以开箱即用的方法。

模式善于表达:一般有一组结构和词汇可以非常优雅地帮助表达相当大的解决方案。

注:模式不是标准的解决方案,模式也不能解决所有的设计问题,也不能代替我们优秀的软件设计师。模式的角色仅仅提供给我们一个解决方案。

三、JavaScript设计模式

1、JavaScript设计模式之观察者模式:

先说一下个人理解:观察者模式又称“发布-订阅(Publish/Subscribe)”,发布与订阅是两个不同对象的功能。在具体编程中,发布者有了新的内容,需要向订阅者推送数据,若订阅者退订了则要对发布者中的订阅者列表进行更新。

观察者模式主要应用于对象之间一对多的依赖关系,当一个对象发生改变时,多个对该对象有依赖的其他对象也会跟着做出相应改变,这就非常适合用观察者模式来实现。使用观察者模式可以根据需要增加或删除对象,解决一对多对象间的耦合关系,使程序更易于扩展和维护。

应用场景:

【1】微信公众号订阅,多个读者订阅一个微信公众号,一旦公众号有更新,多个读者都会收到更新。(邮箱订阅同理)

【2】在《大话设计模式》一书中,提到类似的情况:如果针对发布者内容而订阅者要做不同的事情呢?比如一个按钮和三个矩形,点击按钮的时候,第一个矩形增加宽度,第二个矩形增加高度,第三个矩形则变成圆角矩形又该怎么做呢?当然我们可以在三个矩形的update内部写具体的实现代码,但是这update岂不是没有一个具体的功能描述了吗?该书中用“事件委托”解决了这个问题(此处事件委托和DOM中的事件委托应该是两码事),我个人理解这个“事件委托”在javascript中可以用一个数组表示,然后里面放各个订阅者的不同名字的update,然后一一调用。

代码解读:

--发布者相关信息解读//发布者

  function Publisher(){
        this.observers = [];  
        this.state = "";  
    }

  //增加

    Publisher.prototype.addOb=function(observer){
        var flag = false;
        for (var i = this.observers.length - 1; i >= 0; i--) {
            if(this.observers[i]===observer){
                flag=true;                
            }
        };
        if(!flag){
            this.observers.push(observer);
        }
        return this;
    }

   //移除

    Publisher.prototype.removeOb=function(observer){
        var observers = this.observers;
        for (var i = 0; i < observers.length; i++) {
            if(observers[i]===observer){
                observers.splice(i,1);
            }
        };
        return this;
    }

 //更新

    Publisher.prototype.notice=function(){
        var observers = this.observers;
        for (var i = 0; i < observers.length; i++) {
                observers[i].update(this.state);
        };
    }

--订阅者相关信息解读//订阅者

(订阅者功能很简单,只需要一个更新(update)功能,但是每一个订阅者的更新功能可能不一样,例如应用场景中提到的【2】)

    function Subscribe(){
        this.update = function(data){
              console.log(data);
        };
    }

--为了满足订阅者不同的需求,可以为每一个订阅者的实例设置单独的update。

//实际应用

    var obA = new Subscribe(),
        obB = new Subscribe();

    var pba = new Publisher();

    pba.addOb(obA);
    pba.addOb(obB);

    obA.update = function(state){
        console.log(state+"hello!");
    }
    obB.update = function(state){
        console.log(state+"world!");
    }
    pba.state = "open ";
    pba.notice();

so--彩蛋

一般pm的需求肯定不会这样提。mrd文档一般为第一个input框输入中文姓名拼音,第二个input框(不可写)自动“0881+姓名拼音”,第三个文本框(不可写)自动生成邮箱“拼音+888@163.com

部分代码如下:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div>
        <label>员工姓名拼音:<input type="text" id="pba" placeholder="请输入员工姓名拼音" /></label><br /><br />
        <label>生成编号:<input type="text" id="oba" readonly /></label>
        <label>生成邮箱:<input type="text" id="obb" readonly /></label>
    </div>

    <script type="text/javascript">
        //发布者
        function Publisher(obj){
            this.observers = [];
            var state = obj.value;     //让该内容不能直接访问

            //新增两个对于state的操作 获取/更新
            this.getState=function(){
                return state;
            }
            this.setState=function(value){
                state = value;
                this.notice();
            }
            this.obj = obj;

        }
        Publisher.prototype.addOb=function(observer){
            var flag = false;
            for (var i = this.observers.length - 1; i >= 0; i--) {
                if(this.observers[i]===observer){
                    flag=true;                
                }
            };
            if(!flag){
                this.observers.push(observer);
            }
            return this;
        }
        Publisher.prototype.removeOb=function(observer){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                if(observers[i]===observer){
                    observers.splice(i,1);
                }
            };
            return this;
        }
        Publisher.prototype.notice=function(){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                    observers[i].update(this.getState());
            };
        }

        //订阅者
        function Subscribe(obj){
            this.obj = obj;
            this.update = function(data){
                this.obj.value = data;
            };
        }

        //实际应用
        var oba = new Subscribe(document.querySelector("#oba")),
            obb = new Subscribe(document.querySelector("#obb"));

        var pba = new Publisher(document.querySelector("#pba"));

        pba.addOb(oba);
        pba.addOb(obb);

        oba.update = function(state){
            this.obj.value = “0881”+state;
        }
        obb.update = function(state){
            this.obj.value = state+“888@163.com”;
        }

        pba.obj.addEventListener('keyup',function(){
            pba.setState(this.value);
        });
        
    </script>
</body>
</html>

2、JavaScript设计模式之装饰者模式:

先说一下个人理解:装饰者模式重点在装饰,同样都是女生,如果其中一个女生化个妆涂个口红打扮(装饰)一下即可秒变女神(夺人眼球的功能)。

其实,实现装饰者模式的其中一个方法是使得每个装饰者成为一个对象,并且该对象包含了应该被重载的方法。每个装饰者实际上继承了目前已经被前一个装饰者进行增强后的对象。每个装饰方法在“继承的对象”上调用了同样的方法并获取其值,此外它还继续执行了一些操作。

应用场景:

【1】之前项目遇到过的,创建会员卡,会员卡的外观以及会员卡详情都是一样的功能。但是,对于不同的用户会和会员卡有不一样的联系,例如:未激活,未领取,设置默认卡等。可以针对会员卡这个对象,给予不一样的修饰函数。若后面要对该会员卡还有其他相应的交互,直接装饰即可~

【2】假设我们在编写一个飞机大战的游戏,随着经验值的增加,我们操作的飞机对象可以升级成更厉害的飞机,一开始这些飞机只能发射普通的子弹,升到第二级时可以发射导弹,升到第三级时可以发射原子弹。

部分代码如下:

var Plane = function(){};
    Plane.prototype.fire = function(){
        console.log( '发射普通子弹' );
    }

    var MissileDecorator = function( plane ){
        this.plane = plane;
    }
    MissileDecorator.prototype.fire = function(){
        this.plane.fire();
        console.log( '发射导弹' );
    }
    var AtomDecorator = function( plane ){
        this.plane = plane;
    }
    AtomDecorator.prototype.fire = function(){
        this.plane.fire();
        console.log( '发射原子弹' );
    }

    var plane = new Plane();
    plane = new MissileDecorator( plane );
    plane = new AtomDecorator( plane );
    plane.fire();// 分别输出: 发射普通子弹、发射导弹、发射原子弹

导弹类和原子弹类的构造函数都接受参数plane 对象,并且保存好这个参数,在它们的fire方法中,除了执行自身的操作之外,还调用plane 对象的fire 方法。

因为装饰者对象和它所装饰的对象拥有一致的接口,所以它们对使用该对象的客户来说是透明的,被装饰的对象也并不需要了解它曾经被装饰过,这种透明性使得我们可以递归地嵌套任意多个装饰者对象。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容