0
点赞
收藏
分享

微信扫一扫

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

基础装饰器

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

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

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

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

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

var fire = Plane.fire;

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

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

钢铁侠示例

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

//一个普通人类
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装饰器(语法糖)

  • 给类添加装饰
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)
		}
	}
}
举报

相关推荐

0 条评论