当咱们想要扩展一个对象的能力时,一般能够经过添加原型方法,修改构造函数,继承等方式。除此以外,咱们还能够经过妆饰者模式来达到目的。函数
例如一个游戏角色,咱们在不改变这个角色对象的条件下,给角色穿一件装备(武器),那么角色的属性(攻击力)就会增长。这个过程,就能够由妆饰者模式来完成。this
咱们经过一个例子来演示。spa
首先咱们有几件装备,他们的信息保存在config.js
中,以下:prototype
// config.js export const cloth = { name: '七彩炫光衣', hp: 1000 } export const weapon = { name: '青龙偃月刀', attack: 2000 } export const shoes = { name: '神行疾步靴', speed: 300 } export const defaultRole = { hp: 100, atk: 50, speed: 125, cloth: null, weapon: null, shoes: null, career: null, gender: null }
而后建立一个基础的角色对象。3d
// 基础角色 // 有血条,攻击力,速度三个基础属性 // 以及衣服,武器,鞋子三个装备插槽 var Role = function(role) { this.hp = role.hp; this.atk = role.atk; this.speed = role.speed; this.cloth = role.cloth; this.weapon = role.weapon; this.shoes = role.shoes; }
在原型上添加奔跑和攻击两个共有方法。code
Role.prototype = { constructor: Role, run: function() {}, attack: function() {} // ... }
引入配置文件中的准备与默认角色对象
import { cloth, weapon, shoes, defaultRole } from './config';
建立职业为战士的角色对象。blog
var Soldier = function(role) { var o = Object.assign({}, defaultRole, role); Role.call(this, o); // 构造函数继承 this.nickname = o.nickname; this.gender = o.gender; this.career = '战士'; if (role.hp == defaultRole.hp) { this.hp = defaultRole.hp + 20; } // 战士的移动血条 + 20 if (role.speed == defaultRole.speed) { this.speed = defaultRole.speed + 5; } // 战士的移动速度 + 5 } // 原型的继承 Soldier.prototype = Object.create(Role.prototype, { constructor: { value: Soldier, }, run: { value: function() { console.log('战士的奔跑动做'); }, }, attack: { value: function() { console.log('战士的基础攻击'); } } // ... })
接下来咱们要建立装饰类。继承
由于装饰类可能会有不少,衣服鞋子武器都确定各有一个装饰类来分别负责不一样的行为与变化,因此咱们须要几个基础装饰类。一般状况下,装饰类与被装饰的类有一些类似的地方,你们能够自行体会其中的差别,以下:游戏
// 基础装饰类 var Decorator = function(role) { this.role = role; this.hp = role.hp; this.atk = role.atk; this.speed = role.speed; this.cloth = role.cloth; this.weapon = role.weapon; this.shoes = role.shoes; this.career = role.career; this.gender = role.gender; this.nickname = role.nickname; } Decorator.prototype = { constructor: Decorator, run: function() { this.role.run(); }, attack: function() { this.role.attack(); } // ... }
咱们能够看到,基础装饰类以一个角色对象做为构建基础,并无对角色对象作进一步改变。所以,具体的改变确定是在具体的装饰类中进行的。
接来下建立一个衣服的装饰类,ClothDectorator
,咱们的例子中,装备一件衣服并不会修改其行为,只是增长属性,代码以下:
var ClothDecorator = function(role, cloth) { Decorator.call(this, role); this.cloth = cloth.name; this.hp += cloth.hp; }
衣服装饰类继承基础装饰类,并增长一个装备对象做为构建基础,在构造函数内部,新增了衣服插槽this.cloth
与增长了血条。
咱们在具体使用中感觉一下具体变化:
var base = { ...defaultRole, nickname: 'alex', gender: 'man' } var alex = new Soldier(base); // 新建一个战士角色 alex.run(); // 跑一跑 alex.attack(); // 攻击一下 console.log(alex); // 查看alex对象 alex = new ClothDecorator(alex, cloth); // 装备衣服 console.log(alex); // 查看变化
从下图咱们能够看到具体的变化,说明装饰成功了。
除此以外,咱们还须要建立武器装饰类与鞋子装饰类,武器与鞋子的穿戴会改变角色的攻击动做与奔跑动做,所以须要多行为进行更改,以下:
// 武器装饰类 var WeaponDecorator = function(role, weapon) { Decorator.call(this, role); this.weapon = weapon.name; this.atk += weapon.attack; } WeaponDecorator.prototype = Object.create(Decorator.prototype, { constructor: { value: WeaponDecorator }, attack: { // 修改攻击方法 value: function() { console.log('装备了武器,攻击变得更强了'); } } }) // 鞋子装饰类 var ShoesDecorator = function(role, shoes) { Decorator.call(this, role); this.shoes = shoes.name; this.speed += shoes.speed; } ShoesDecorator.prototype = Object.create(Decorator.prototype, { constructor: { value: ShoesDecorator }, run: { // 修改奔跑方法 value: function() { console.log('穿上了鞋子,奔跑速度更快了'); } } })
角色alex穿了衣服以后,咱们还能够继续为他穿上鞋子与武器。代码以下:
console.log(' '); console.log('------装备武器-----'); alex = new WeaponDecorator(alex, weapon); // 装备武器 alex.attack(); console.log(alex); console.log(' '); console.log('------装备鞋子-----'); alex = new ShoesDecorator(alex, shoes); // 装备鞋子 alex.run(); console.log(alex);
OK,这就是整个装饰者模式的思路与具体实现,
用ES6的class实现,源代码以下:
import { cloth, weapon, shoes, defaultRole } from './config'; // 基础角色 class Role { constructor(role) { this.hp = role.hp; this.atk = role.atk; this.speed = role.speed; this.cloth = role.cloth; this.weapon = role.weapon; this.shoes = role.shoes; } run() {} attack() {} } class Soldier extends Role { constructor(roleInfo) { const o = Object.assign({}, defaultRole, roleInfo); super(o); this.nickname = o.nickname; this.gender = o.gender; this.career = '战士'; if (roleInfo.hp == defaultRole.hp) { this.hp = defaultRole.hp + 20; } if (roleInfo.speed == defaultRole.speed) { this.speed = defaultRole.speed + 5; } } run() { console.log('战士的奔跑动做'); } attack() { console.log('战士的基础攻击'); } } // class Mage extends Role {} class Decorator { constructor(role) { this.role = role; this.hp = role.hp; this.atk = role.atk; this.speed = role.speed; this.cloth = role.cloth; this.weapon = role.weapon; this.shoes = role.shoes; this.career = role.career; this.gender = role.gender; this.nickname = role.nickname; } run() { this.role.run(); } attack() { this.role.attack() } } class ClothDecorator extends Decorator { constructor(role, cloth) { super(role); this.cloth = cloth.name; this.hp += cloth.hp; } } class WeaponDecorator extends Decorator { constructor(role, weapon) { super(role); this.weapon = weapon.name; this.atk += weapon.attack; } attack() { console.log('装备了武器,攻击变得更强了'); } } class ShoesDecorator extends Decorator { constructor(role, shoes) { super(role); this.shoes = shoes.name; this.speed += shoes.speed; } run() { console.log('穿上了鞋子,奔跑速度更快了'); } } const baseInfo = { ...defaultRole, nickname: 'alex', gender: 'man' } let alex = new Soldier(baseInfo); alex.run(); alex.attack(); console.log(alex); console.log(' '); console.log('------装备衣服-----'); alex = new ClothDecorator(alex, cloth); console.log(alex); console.log(' '); console.log('------装备武器-----'); alex = new WeaponDecorator(alex, weapon); alex.attack(); console.log(alex); console.log(' '); console.log('------装备鞋子-----'); alex = new ShoesDecorator(alex, shoes); alex.run(); console.log(alex);
除了角色与装备之间的关系能够用装饰者模式来搞定以外,咱们在玩游戏的时候,还知道每一个角色都会在某些状况下得到不一样的buff,例如大龙buf,小龙buf,红buff,蓝buff等,这些buff有的会更改角色属性,例如cd更短,攻击更高,有的会更改攻击特性,例如红buff会持续掉血,减速等,这些buff还有持续时间,你们能够思考一下,如何使用装饰者模式来完成这些buff的实现。欢迎你们留言提供思路。