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


    前言:

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

    单例模式

    单例是保证一个类只创建一个实例,实例不存在新建一个实例,实例存在返回已经存在的实例。  单例模式很好理解,使用情况也很多,比如我最近做的ng4的项目,会定义一些service(ng的service都是单例),service里面存放的数据供全局使用,所有组件共享这个service。  再说一个应用实例: 全局toast框,我们只需要每次调用同一个实例改变toast框里的文本,并控制其隐藏显示。

    工厂模式

    下面是书中工厂模式的例子,关于它的实现和优劣我就不说了,这里只是以个人的理解来说为什么它叫工厂模式。  创建对象的时候,都会先新建一个新的原生对象,再对它进行属性赋值,最后返回一个成品对象。所有属性和方法都在它内部,都是它唯一拥有的。那么传统工厂生产出来的电视手机等这些硬件,他们就是这种模式,他们的在出厂前就会在产品内部定义好所有的组件,最后加工为成品。所以我们就这样来理解工厂模式的命名吧。

    function createPerson(name, age, job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
    alert(this.name);
    };
    return o;
    }
    var person1 = createPerson("Nicholas", 29, "Software Engineer");
    var person2 = createPerson("Greg", 27, "Doctor");

    建造者模式

    使用多个简单的对象一步一步构建成一个复杂的对象。它使组装过程和每个部件的开发分离开来。

    直接举个例子: 假如我们要设计一个h5飞机游戏,那么我们设计一款飞机的前端代码,把飞机先简单拆分为如下两个部件: 机体、子弹。每个部件定义一个对象,然后设置它们的参数(比如飞机图片地址,子弹设置伤害100的威力值和子弹图片地址),最后通过组装代码逻辑来组装成一架完整的游戏飞机。 那么飞机是一个复杂对象,这两个部件就是更单纯更简单的对象。来点代码示例:

    function plane(){
      this.buildBodyModule();
    this.buildBulletModule(); } plane.prototype = { buildBodyModule: function(){ this.body = {
    imgUrl:'xxx.png',
    destoryImgUrl: 'xx2.png'
    } },
    buildBulletModule: function(){ this.bullet = {
    imgUrl: 'xx3.png',
    power: 100
    } } }

    当需求变动的时候,我们只需修改对应的单个部件,甚至可以随时在移除或者添加其它部件。 所以这样我们就大致能理解为什么它叫建造者模式了,现实生活中,一个复杂工程,就比如汽车、建筑、飞机等,他们都是由不同的精细设计的部件通过合理的组装才生产出成品的。  建造者模式和装饰者模式看起来实际有点类似,下面就说装饰者模式。

    装饰者模式

    装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。  其实装饰者模式就是通过一个特定的方法,给一个实例对象添加特定的功能。比如一个基础汽车类,有引擎、轮胎和车架,代码如下:

    function BaseCar(){}
    BaseCar.prototype = {
      engine: e300,
      tyre: t200,
      frame: f10
    }

    我们在车的基础上,贴一些装饰纸,以下代码就是最基础的装饰者模式的使用。

    var car = new BaseCar();
    setStyle(car);
    
    setStyle(carObj){
      carObj.style = '达康书记玻璃贴纸';
    }

     所谓装饰者模式,就是通过一个外部的方法,来包装一个对象,并且扩展它。

    装饰者模式 VS 建造者模式

     实际装饰者模式和建造者模式,都有类似分步的方式去建立一个对象。而建造者模式是在类的内部,一步步建造对象,很强调整体性和逻辑性,当然我上面的例子并没有体现逻辑性之说,如果我们把飞机的机体再拆分一下,我们可能需要先建立机体的机舱(图片),再根据机舱位置,放置或者说定位侧翼和尾翼图片位置,那么这就是所谓逻辑性了。  而装饰者模式,是传入对象到外部函数,通过函数体内部对对象进行扩展。其实通过外部函数,也能达到逻辑性的要求,通过外部函数的多次调用,让对象添加和迭代功能。但是如果所有的组装过程都是通过外部函数进行,在对象属性和子对象的引用上可能会让代码变得非常复杂,甚至可能需要函数里返回对象,再用另一个函数包裹这类的函数嵌套,整体的逻辑从代码层面看来也会变得复杂和难以理解。  对于装饰者模式的使用,还是应该如它的名字一样,就应该是为一个基础功能完整的对象添加一些额外的扩展。  在面向对象编程的程序里,建造者模式和装饰者模式应该是常会结合在一起使用,把上面的汽车例子,改编成一个更像这两模式结合的代码:

    function BaseCar(){
        this.buildFrame();
        this.buildEngine();
        this.buildTyre();
    }
    BaseCar.prototype = {
      buildFrame: function(){
        this.frame = 'f10';
        //todo sth
      },
      buildEngine: function(){
          this.engine = 'e300';
          //todo sth
      },
      buildTyre: function(){
        this.tyre = 't200';
        //todo sth
      }
    }
    
    var car = new BaseCar();
    setStyle(car);
    
    setStyle(carObj){
      carObj.style = '达康书记玻璃贴纸';
    }

    外观模式 门面模式

    外观模式(Facade),为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使得这一子系统更加容易使用。  这是百度百科的定义描述。唉,其实这个模式很好理解,但是我一开始看到这个描述我就真的看不懂。“为接口提供一个界面”,这是什么描述,作为一个前端,界面就会理解成页面或者肉眼能看到的界面。为接口提供一个界面是什么gui。。  好吧,再看下结构图:

    看了这个图实际还不能彻底对它进行定性,当我再看到下面的代码示例的时候,我就彻底清楚了外观模式的定义:

    function SubSystemOne(){
        //xxx
    }  
    function SubSystemTwo(){
        //xxx
    }  
    function SubSystemThree(){
        //xxx
    }  
      
    facade(){  
        SubSystemOne();  
        SubSystemTwo();  
        SubSystemThree();  
    }

    外观模式是把一系列逻辑封装到一个方法中,使当前逻辑更易使用,更易维护。那么我们实际前端开发中,会常常用到,说一个前端开发中的例子: 一个后台管理系统,有一个用户列表页面,添加用户和修改用户信息,触发添加或者修改按钮的时候,都是使用的同一个弹窗模板,然后根据不同的传参去判断是否是新增或者修改,再去改变弹框模板表单里的数据,添加的时候所有表单为空,编辑的时候把之前数据载入到表单。那么这个弹窗模板就是门面、也就是上图的Facade。表单的html代码是不变的,没有必要写两个模板,所以这也可以叫代码去重。

    再举一个例子: 上面说的弹框模板的表单,在新增用户成功的回调函数里我们需要把表单里的数据给重置了,代码如下:

     
    addUser(){
      //获取form数据, addModel 设置参数
      addModel(function(){
          //添加成功回调
          this.nodes.$form.address.val('');
          this.nodes.$form.cell.val('');
          this.nodes.$form.name.val('');
      })
    }

    这时编辑成功后也需要去执行上面回调里的重置代码,我平时为了代码去重,就需要写一个单独的重置方法,在两个回调里调用,代码如下:

    addUser(){
      //获取form数据, addModel 设置参数
      addModel(function(){
          //添加成功回调
          reset()
      })
    }
    editUser(){
      //获取form数据, editModel 设置参数
      editModel(function(){
          //添加成功回调
          reset()
      })
    }
    
    function reset(){
      this.nodes.$form.address.val('');
      this.nodes.$form.cell.val('');
      this.nodes.$form.name.val('');
    }

    reset方法就是Facade,甚至说有更复杂的需求的时候,reset方法还可以传参数,根据参数来重置某几个表单,总之reset方法把重置表单的逻辑封装在此方法内,其他地方需要调用重置相关功能,都用经过它才行,这样可以减少代码的耦合性,去除很多重复代码,后期维护也非常清晰、改动也方便。  JQ的 $ 选择器,其实也是此模式,它专门处理DOM选择,集合了id、class等等选择器,我们只需要在传参数的时候前面加上#或者.,$('#id'),$('.class'),就能选择相应的dom。

    结语:

    其实设计模式并不是很神秘,很牛逼冲天的技巧,也许你在不经意间写出的代码就是一种模式,这些设计模式只是针对一些写法做了定义和命名。  这是此系列博客的第一篇。我不知道自己描述出来的设计模式,是否能被广大同行所接受或者让人能看明白。  如果你觉得此博客对你有帮助,欢迎留言或者点击推荐,更多的反馈和支持可能是我坚持写下去的理由和动力!

     

    此系列博客目录:

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

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

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

  • 相关阅读:
    dfs手写栈模板
    Remember the Word
    Sockets
    Sanatorium
    Exams
    Cormen — The Best Friend Of a Man
    win 7 普通家庭版 装IIS
    [引]构造文法时表达式中算符优先级的问题
    Chart系列(二):数据绑定
    算法整理篇之:数据结构 | 数组(1)
  • 原文地址:https://www.cnblogs.com/1wen/p/7403538.html
Copyright © 2020-2023  润新知