當咱們想要擴展一個對象的能力時,一般能夠經過添加原型方法,修改構造函數,繼承等方式。除此以外,咱們還能夠經過妝飾者模式來達到目的。函數
例如一個遊戲角色,咱們在不改變這個角色對象的條件下,給角色穿一件裝備(武器),那麼角色的屬性(攻擊力)就會增長。這個過程,就能夠由妝飾者模式來完成。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的實現。歡迎你們留言提供思路。