资讯专栏INFORMATION COLUMN

MobX详解(一):装饰者模式

xingqiba / 3250人阅读

摘要:当我们想要扩展一个对象的能力时,通常可以通过添加原型方法,修改构造函数,继承等方式。构造函数继承战士战士的移动血条战士的移动速度原型的继承战士的奔跑动作战士的基础攻击接下来我们要创建装饰类。

当我们想要扩展一个对象的能力时,通常可以通过添加原型方法,修改构造函数,继承等方式。除此之外,我们还可以通过妆饰者模式来达到目的。

例如一个游戏角色,我们在不改变这个角色对象的条件下,给角色穿一件装备(武器),那么角色的属性(攻击力)就会增加。这个过程,就可以由妆饰者模式来完成。

我们通过一个例子来演示。

首先我们有几件装备,他们的信息保存在config.js中,如下:

// 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
}

然后创建一个基础的角色对象。

// 基础角色
// 有血条,攻击力,速度三个基础属性
// 以及衣服,武器,鞋子三个装备插槽
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;
}

在原型上添加奔跑和攻击两个共有方法。

Role.prototype = {
    constructor: Role,
    run: function() {},
    attack: function() {}
    // ...
}

引入配置文件中的准备与默认角色

import { cloth, weapon, shoes, defaultRole } from "./config";

创建职业为战士的角色对象。

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的实现。欢迎大家留言提供思路。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107070.html

相关文章

  • MobX详解(二):ES7 装饰器 decorator

    摘要:在学习装饰器语法之前,需要先温习一下的一些基础知识。函数最后必须返回。使用时也很简单,如下在方法前面加上,就是装饰器语法。装备了,攻击更强了。职业的基本攻击穿上了,移动速度更快了。 在学习ES7装饰器语法之前,需要先温习一下ES5的一些基础知识。 假设有对象如下:(便于理解) var person = { name: TOM } 在ES5中,对象中的每个属性都有一个特性值来描述...

    Keagan 评论0 收藏0
  • 【用故事解读 MobX 源码(四)】装饰器 和 Enhancer

    摘要:所以这是一篇插队的文章,用于去理解中的装饰器和概念。因此,该的作用就是根据入参返回具体的描述符。其次局部来看,装饰器具体应用表达式是,其函数签名和是一模一样。等装饰器语法,是和直接使用是等效等价的。 ================前言=================== 初衷:以系列故事的方式展现 MobX 源码逻辑,尽可能以易懂的方式讲解源码; 本系列文章: 《【用故事解...

    maybe_009 评论0 收藏0
  • Java设计模式装饰模式详解

    摘要:装饰者模式是动态地将责任附加到对象上。然后我们在子类计算价格的时候加上父类中计算好的配料的价格。结果可乐加冰可乐加冰加糖在的类库中就有很多实际应用到了装饰模式,比如就可以用来装饰,提供更加强大的功能。 装饰者模式是动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。 假设我们有一个需求,是给一家饮料店做一个计算各种饮料价格的功能。听起来很简单,我们创建一个抽象...

    sewerganger 评论0 收藏0
  • 走过的,路过的,快来看看laravel设计模式好文章的汇总!

    摘要:中用到了很多设计模式,实现这些设计模式时也用到了不少新特性。学会之后可以把设计模式的思想应用在我们的程序中。实际上是服务容器中那些底层类的静态代理,相比于传统的静态方法,在提供了简洁且丰富的语法同时,还带来了更好的可测试性和扩展性。 laravel中用到了很多设计模式,实现这些设计模式时也用到了不少php新特性。学习这些设计模式,可以帮助我们: 学习php新特性,知道如何用新特性,写...

    2i18ns 评论0 收藏0
  • 前端每周清单第 55 期: MobX 4 特性概览,iOS Hacks 分享, 分布式事务详解

    摘要:异步剪贴板操作过去的数年中,各浏览器基本上都在使用来进行剪贴板交互。而提供了新的,则为我们提供了另一种异步式的剪贴板操作方式,本文即是对该机制与接口规范的详细介绍。 showImg(https://segmentfault.com/img/remote/1460000013854167); 前端每周清单第 55 期: MobX 4 特性概览,iOS Hacks 分享, 分布式事务详解 ...

    zombieda 评论0 收藏0

发表评论

0条评论

xingqiba

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<