• 前端也要学系列:设计模式之装饰者模式


    什么是装饰者模式

    今天我们来讲另外一个非常实用的设计模式:装饰者模式。这个名字听上去有些莫名其妙,不着急,我们先来记住它的一个别名:包装器模式

    我们记着这两个名字来开始今天的文章。

    首先还是上《设计模式》一书中的经典定义:

    1. 动态地给一个对象添加一些额外的职责。
    2. 就增加功能来说,装饰者模式相比生成子类更为灵活。

    我们来分析一下这个定义。

    给对象添加一些新的职责,我们很容易想到创建子类来继承父类,然后在子类上增加额外的职责。

    那什么是动态地呢?应该就是说这些新添加的职责在类一开始创建的时候我们并不知道,而是在使用过程根据需要而添加的。

    相比生成子类更为灵活,这句话让装饰者模式和子类继承赤裸裸的刀兵相见了。没有对比就没有伤害,那我们就用例子来验证这句话。

    传统面向对象的实现

    我们假设你是以为已经走上人生巅峰的汽车生产商,你的公司生产各种用途的汽车,某一天一个客户下单了四种汽车,分别是家用轿车、SUV、旅行车和跑车。我们很轻松地像下面这样进行交付了。

    var Car = function(){}
    
    Car.prototype.start = function(){
        console.log("轰轰轰,启动正常!")
    }
    
    var Sedan = new car();// 小轿车
    var Suv = new Car();// SUV
    var Wagon=new Car();// 旅行车
    var Roadster=new Car();// 跑车
    //是不是又学会了几个英文单词?
    

    过了几天客户找来了,说最近人们爱上了西藏自驾游,人们都希望能够选装一些方便越野和载物的功能,比如加装雪地胎、行李箱,升高底盘。

    有经验的你满口答应下来,这个简单,于是你交付了下面的代码:

    //SUV
    Suv.prototype.changeTire = function(){
    	console.log("我换了雪地胎");
    } 
    Suv.prototype.addHeight = function(){
    	 console.log("我升高了底盘");
    }
    Suv.prototype.addBox = function(){
    	console.log("我安装了行李箱");
    }
    //Wagon
    Wagon.prototype.changeTire = function(){
    	console.log("我换了雪地胎");
    } 
    Wagon.prototype.addHeight = function(){
    	 console.log("我升高了底盘");
    }
    Wagon.prototype.addBox = function(){
    	console.log("我安装了行李箱");
    }
    //Sedan
    Sedan.prototype.changeTire = function(){
    	console.log("我换了雪地胎");
    } 
    Sedan.prototype.addHeight = function(){
    	 console.log("我升高了底盘");
    }
    Sedan.prototype.addBox = function(){
    	console.log("我安装了行李箱");
    }
    
    // 使用
    var suv = new Suv();
    suv.changeTire();
    suv.addHeight();
    suv.addBox();
    suv.start();
    ...
    

    你增加了多少种特性?3x3=9种。

    你又问,我直接把这三个特性加在Car上不行吗?就不用这么麻烦了。

    当然不行,因为我们还有一种车:Roadster跑车。

    你能想象法拉利换了雪地胎背上行李箱升高底盘是个什么死样子吗?这么干的人肯定疯了。

    如果我们把特性一股脑加在Car上,就避免不了这种情况的发生。

    这个时候,就体现出子类继承的不灵活之处。

    下面,装饰者模式就要正式登场了。

    var Car=function (){}
    
    Car.prototype.start=function(){
        console.log("轰轰轰,启动正常!")
    }
    
    // 创建装饰类(包装类)
    
    var ChangeTireDec=function(car){
    	this.car=car;
    }
    var AddHeightDec=function(car){
    	this.car=car;
    }
    var AddBoxDec=function(car){
    	this.car=car;
    }
    
    // 装饰类具有和Car同样的特性,只不过额外执行了一些其他的操作
    
    ChangeTireDec.prototype.start=function(){
        console.log("我换了雪地胎");
        this.car.start();
    }
    
    AddHeightDec.prototype.start=function(car){
        console.log("我升高了底盘");
        this.car.start();
    }
    AddBoxDec.prototype.start=function(car){
        console.log("我安装了行李箱");
        this.car.start();
    }
    
    // 使用
    var suv=new Suv();
    
    suv=new ChangeTireDec(suv);
    suv=new AddHeightDec(suv);
    suv=new AddBoxDec(suv);
    
    suv.start();
    

    上面的代码你增加了几种特性?只有三种!而且不管你是给SUV还是Wagon还是Sedan加装,都不需要再增加特性的代码。

    这,就是装饰者模式的优势所在。

    现在我们再回过头来看看GoF的定义:

    1. 动态地给一个对象添加一些额外的职责。
    2. 就增加功能来说,装饰者模式相比生成子类更为灵活。

    怎么样,是不是如同1+1=2一样简单了?现在你应该也明白了为什么装饰者模式又叫座包装器模式了。因为它将类的原有特性包装起来,添加其他的特性,就像一个箱子一样。而且实现过程中,还满足了封闭-开放原则。

    JavaScript的实现

    上面的例子中,我们是模拟了传统的面向对象语言来解释什么是装饰者模式。我们都知道,要动态改变JavaScript对象非常容易,可以向操作变量一个操作对象,我们再来改写下上面的例子,让它更javasripty

    var car = {
    	start: function(){
        	console.log("轰轰轰,正常启动!");
        }
    }
    
    var ChangeTireDec = function(){
    	    console.log("我换了雪地胎");
    }
    var AddHeightDec = function(){
    	console.log("我升高了底盘");
    }
    var AddBoxDec = function(){
    	 console.log("我安装了行李箱");
    }
    
    var start1 = car.start;
    car.start=function(){
        ChangeTireDec();
        start1();
    }
    
    var start2=car.start;
    car.start = function(){
        AddHeightDec();
        start2();
    }
    
    var start3=car.start();
    car.start = function(){
        AddBoxDec();
        start3();
    }
    
    // 执行
    
    car.start();
    
    

    实际中的应用

    从上面的例子我们可以看出来,我们不断的将car.start的引用赋值给临时变量,然后将原来的car.start指向新的对象--包含了原来对象的引用和新的特性的对象。这很好的保证了代码的开放-封闭原则,这是今天第二次提到这个原则了,就是对修改封闭,对新增开放。

    特别当你要重构一个非常复杂的多人项目时,如果你不想因为修改了同事的一行代码而引起“蝴蝶效应”,那么将他的方法整个打包赋值然后用装饰者模式增加新的功能,是一种非常安全而且高效的做法。

    下一步,我们可以愉快的去使用装饰者模式啦!

  • 相关阅读:
    计算机中的那些缩写词
    Linux 下dns的搭建
    html中的定位
    编程基础之流程控制
    linux中kvm的安装及快照管理
    zabbix 中监控windows 的typepref中的值
    详解Linux交互式shell脚本中创建对话框实例教程_linux服务器
    rhel7 单用户修改root密码
    Linux vim编辑器使用详解
    Linux下用ftp更新web内容!
  • 原文地址:https://www.cnblogs.com/depsi/p/9102888.html
Copyright © 2020-2023  润新知