设计模式梳理之装饰器模式

其实每个设计模式的名字都非常准确的表达了中心思想,装饰器模式,那中心思想就是装饰,装饰什么呢,装饰我们原有的类或者方法,使类和方法拥有更多的能力和可能性,完成一些原本没能力完成的事情,或者说把原本的事情完成的更出色

我们看下装饰器模式的定义:装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

基础装饰器

装饰器,作为装饰,要做的就是要锦上添花,有的人会问,那我继承下来超类,自己再添加一些不同的方法和属性,或者把原方法和属性重写一些,也可以做到锦上添花的效果啊,那这里就有两点需要声明一下了

  • 1.我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
  • 2.通过继承的方式会导致强耦合,超类的改变会导致所有的子类都跟随改变

我们通过最原生的基础来着手看一下装饰器模式

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

var missileDecorator= function () {
    console.log('发射导弹!');
};

var fire = Plane.fire;

Plane.fire=function () {
    fire();
    missileDecorator();
};

Plane.fire();
//输出结果为,"发射普通的子弹" "发射导弹"

由示例可以看出,我们Plane类的fire方法做了装饰,调用依旧是原本的调用习惯,但是却多了一个"发射导弹",这也侧面看出装饰器的一个特点,透明,而且有一个优点,就是我们可以再装饰的fire中去判断要不要添加装饰,也就是通过条件判断是否要执行missileDecorator

钢铁侠示例

以钢铁侠示例,我们可以把原本的人作为原生的类,原生的人由什么能力,胳膊,腿,奔跑,拳击诸如此类的能力,但是我们可以通过装饰,也就是钢铁侠的套装,来给原本的人,来添加能力

//一个普通人类
class Person(){
	constructor() {
    	this.name = 'Iron Man'
  	},
  	hand(){
  		console.log('我能抓取东西')
  	},
  	leg(){
  		console.log('我能奔跑')
  	}
}
//机械手装饰器
class Manipulator(){
	constructor(person) {
		this.oldHand= person.hand
        Person.hand= this.hand
    }

    hand() {
    	this.oldHand()
        console.log('发送冲击波')
    }
}

//机械腿装饰器
class Mechanicalleg(){
	constructor(person) {
		this.oldLeg= person.leg
        Person.leg= this.leg
    }

    leg() {
    	this.oldLeg()
        console.log('我能飞了')
    }
}
//普通人出现
var person= new Person()
person.hend()//我能抓取东西
//给普通人装饰机械手臂
var Mechanicalperson= new Manipulator(person)
Mechanicalperson.hend()//发送冲击波
//给普通人装饰机腿
var Mechanicalperson= new Mechanicalleg(person)
Mechanicalperson.leg()//我能飞了

有人会问,不能装完手装腿吗,只能用一个吗,当然不是的,装饰器可以多层装饰,当然也可以做一个机械套装的装饰,把两个装饰器组合一下,都是可以的,固定例子但是灵活运用才可以达到效果嘛

函数装饰封装

我们调用的函数有时候会有特定的需求,我们该如何让调用函数时自动调用另外一个函数,这样的能力装饰器也是可以完成的,无论是函数调用前的逻辑处理还是函数调用后的逻辑处理,都是可以完美实现的,我们看例子

Function.prototype.before=function (beforefn) {
    var _this= this;
    return function () {
    	beforefn.apply(this,arguments);
    	return _this.apply(this,arguments);
    };
};
//使用举例,formSubmit和validata都为函数
formSubmit= formSubmit.before(validata);
  • Function的prototype挂载before方法,其实跟装饰器的封装没有关系,我们前边也梳理过原型模式,总之就是为了让所有的函数都可以正常调用到before方法而已
  • apply问题.这个是编程的基础,关于this的指向问题,和call都是一样的改变指针指向的作用,只不过是apply是接受数组形式的参数
  • this指向问题,使用闭包储存了this指向,这个储存起来的this指向永远指向老函数,也就是调用before的函数的对象(例子中的formSubmit),看一下下边返回函数的内容,beforefn的this指向被临时改为返回函数的this,arguments为参数,保证beforefn的this不丢失,被正常调用,而后使用闭包的this调用formSubmit,也就达成了目的
ES7装饰器(语法糖)

前情提要:es7的装饰器属于语法糖 所以我们在项目中使用的时候要借助babel插件编译

ES7de装饰器的使用非常简单,其本质上就是一个函数

  • 给类添加装饰
function leg(target, name, descriptor) {
  	console.log(target,name,descriptor)
}

@leg// 装饰类的装饰器
class Person{
  	leg() {
    	console.log('Person')
  	}
}

const person= new Person()
person.leg()
  • 给方法添加装饰
function leg(target, name, descriptor) {
  	console.log(target,name,descriptor)
}


class Person{
	@leg// 装饰方法的装饰器
  	leg() {
    	console.log('Person')
  	}
}

const person= new Person()
person.leg()

实际打印会发现,当给类添加装饰和给方法添加装饰的时候,参数是不相同的

  • 在装饰类的时候,第一个参数表示类的函数本身,后两个参数为undefined
  • 再装饰方法的时候,第一个参数表示类的原型(prototype), 第二个参数表示方法名, 第三个参数表示被装饰参数的属性
  • 第三个参数内有如下属性:
    configurable - 控制是不是能删、能修改 descriptor 本身。
    writable - 控制是不是能修改值。
    enumerable - 控制是不是能枚举出属性。
    value - 控制对应的值,方法只是一个 value 是函数的属性。
    get 和 set - 控制访问的读和写逻辑。
装饰器工厂

装饰器工厂,那顾名思义肯定是要产出不同的装饰器的,既然装饰器是可以接收参数的,那说明接收参数的装饰器就是可以产出不同的"自己"

@Test('hello')
class Hello {}
function Test(str) {
   return function(){
        target.prototype.a = str;
        target.prototype.f = function(){
            console.log(str)
        };
   }

}
let o = new Hello();
o.f() ==>"hello"
console.log(o.a) ==>"hello"
  • 从最简单的模式来看,装饰器接收参数之后,通过return函数的方式来返回不同的装饰器,是完全可以做到的,这也就是装饰器工厂
  • 同时ES6也有能力分别去添加新方法和重载旧方法,添加新方法上述的例子中就有,重载旧方法在末尾放一个,很好理解,也就不做赘述了
function Test(target) {
	//通过extends方式来继承和重写被装饰类中的方法
	return class extends target{
		f(){
			console.log('我是装饰器方法',this.a)
		}
	}
}

总结:在ES7中有装饰器的环境下,我们很容易就忽视了装饰器模式的的基本概念,封装的越好,就越是使我们的开发效率更高,但是长时间不关注底层原理,就会导致地基不牢,所以还是敞开怀抱接受最方便的封装,但是低下头一定不要忘了多看看基本的理念

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值