ES6 +中的结构设计模式,以《权力的游戏》为例



朋友们,美好的一天!

结构设计模式用于构建对象之间关系的大型系统,以保持灵活性和效率。让我们看看其中一些与《权力的游戏》的引用。

在软件开发中,设计模式用于解决最常见的问题。它们代表开发人员在测试应用程序和修复错误的过程中长期开发的最佳实践。

在本文中,我们将讨论结构模式。它们旨在通过定义与实例进行交互的简单方法来设计应用程序。

最常见的是以下模式:

  • 适配器
  • 装饰器
  • 正面
  • 自适应(轻量级(元素),Flyweight)
  • 代理

适配器


适配器是一个模板,允许您将一个类的接口转换(转移)到另一个类。它允许类一起工作,由于结构不兼容,这通常是不可能的。

想象一下,塔加里安决定与所有可支配的部队(无暇和巨龙)作战,而丹妮莉丝正在寻找与他们互动的方式。

我们将需要以下内容:

  • 纯洁的阶级
  • 龙类
  • 适配器类将Dragon类的burn方法传递给kill常用方法

class Unsullied {
    constructor(name) {
        this.name = name
        this.kill = this.kill.bind(this)
    }

    kill() {
        console.log(`Unsullied ${this.name} kill`)
    }
}

class Dragon {
    constructor(name) {
        this.name = name
        this.burn = this.burn.bind(this)
    }

    burn() {
        console.log(`Dragon ${this.name} burn`)
    }
}

class DragonAdapter extends Dragon {
    kill() {
        this.burn()
    }
}

(() => {
    const Army = [
        new DragonAdapter('Rhaegal'),
        new Unsullied('Grey worm'),
        new DragonAdapter('Drogon')
    ]
    Army.forEach(soldier => soldier.kill())
})()

使用案例


  • 当新的组件或模块应与应用程序中现有的组件一起使用时,或者由于重构而对部分代码进行了改进时,应与旧的部分进行交互。
  • 当您找到用于解决次要问题的库并将其集成到您的应用程序中时。

装饰器


装饰器是一个模板,旨在向现有类动态添加行为或功能。这是子分类的替代方法。

假设我们要检查送给乔佛里国王的饮料是否中毒。

我们将需要以下内容:

  • 国王级
  • 乔佛里国王案
  • 饮料课
  • 类中毒饮料
  • isNotPoisoned函数可拯救国王的生命

function isNotPoisoned(t, n, descriptor) {
    const original = descriptor.value
    if(typeof original === 'function') {
        descriptor.value = function(...args) {
            const [drink] = args
            if(drink.constructor.name === 'poisonedDrink') throw new Error('Someone wants to kill the king')
            return original.apply(this, args)
        }
    }
    return descriptor
}

class PoisonedDrink {
    constructor(name) {
        this.name = name
    }
}

class Drink {
    constructor(name) {
        this.name = name
    }
}

class King {
    constructor(name) {
        this.name = name
    }

    //     
    //      
    @isNotPoisoned
    drink(drink) {
        console.log(`The king drank ${drink}`)
    }
}

(() => {
    const joffrey = new King('Joffrey Baratheon')
    const normalDrink = new Drink('water')
    const poisonedDrink = new Drink('poisoned water')
    joffrey.drink(normalDrink)
    joffrey.drink(poisonedDrink)
})()

装饰器用例


  • 当我们想向具有不同上下文的大量类或方法添加函数时
  • 当我们想改善以前创建的类,但又没有时间进行完全重构时

正面


Facade-在图书馆中广泛使用的模板。它用于提供统一和简单的界面,并隐藏其子系统或子类的复杂性。

想象我们要在与野蛮人的战斗中控制一支军队。我军的某些实体可以移动骑兵,士兵和巨人。但是我们被迫分别调用这些方法,这需要很多时间。我们如何使军队管理更容易?

我们将需要以下内容:

  • 陆军标本
  • 陆军立面课

class Horse {
    constructor(name) {
        this.name = name
    }
    attack() {
        console.log(`Infantry ${this.name} attack`)
    }
}

class Soldier {
    constructor(name) {
        this.name = name
    }
    attack() {
        console.log(`Soldier ${this.name} attack`)
    }
}

class Giant {
    constructor(name) {
        this.name = name
    }
    attack() {
        console.log(`Giant ${this.name} attack`)
    }
}

class ArmyFacade {
    constructor() {
        //  ,           
        this.army = [];
        (new Array(10)).fill().forEach((_, i) => this.army.push(new Horse(i + 1)));
        (new Array(10)).fill().forEach((_, i) => this.army.push(new Soldier(i + 1)));
        (new Array(1)).fill().forEach((_, i) => this.army.push(new Giant(i + 1)));
        this.getByType = this.getByType.bind(this)
    }
    getByType(type, occurency) {
        return this.army.filter(el => {
            return el.constructor.name === type && occurency-- > 0
        })
    }
    attack(armyInfo = {}) {
        const keys = Object.keys(armyInfo)
        let subArmy = []
        keys.forEach(soldier => {
            switch(soldier) {
                case 'horse': {
                    subArmy = [...subArmy, ...this.getByType('Horse', armyInfo.horse)]
                    break;
                }
                    case 'soldier': {
                    subArmy = [...subArmy, ...this.getByType('Soldier', armyInfo.soldier)]
                    break;
                }
                    case 'giant': {
                    subArmy = [...subArmy, ...this.getByType('Giant', armyInfo.giant)]
                    break;
                }
            }
        })
        subArmy.forEach(soldier => soldier.attack())
    }
}

(() => {
    const army = new ArmyFacade()
    army.attack({
        horse: 3,
        soldier: 5,
        giant: 1
    })
})()

用例


  • 当我们想将许多行代码(可能重复)转换为一个简单函数时。

机会主义者


自适应-一种模板,旨在在许多小对象之间进行有效的数据传输。它用于提高性能并节省内存。

假设我们要控制一群白人步行者。同时,我们的步行者可以具有三种状态:

  • 复活

我们将需要以下内容:

  • WhiteWalker班
  • 类WhiteWalkerFlyweight
  • 白步行者复活的客户

class WhiteWalker {
    constructor({
        sprite,
        someOtherBigInformation
    }) {
        this.sprite = sprite
        this.someOtherBigInformation = someOtherBigInformation
        this.state = 'alive'
        this.resurrect = this.resurrect.bind(this)
        this.kill = this.kill.bind(this)
        this.getState = this.getState.bind(this)
    }
    kill() {
        this.state = 'dead'
    }
    resurrect() {
        this.state = 'resurrected'
    }
    getState() {
        return this.state
    }
}

const whiteWalker = new WhiteWalker({
    sprite: Date.now()
})

class WhiteWalkerFlyweight {
    constructor(position, name) {
        this.position = position
        this.name = name
        this.whiteWalker = whiteWalker
    }
    getInfo() {
        console.log(`The White Walker ${this.name} whit sprite ${this.whiteWalker.sprite} is ${this.whiteWalker.state}`)
    }
    getFatherInstance() {
        return this.whiteWalker
    }
}

(() => {
    const myArmy = []
    for(let i = 0; i < 10; i++) {
        myArmy.push(new WhiteWalkerFlyweight({
            x: Math.floor(Math.random() * 200),
            y: Math.floor(Math.random() * 200),
        }, i + 1))
    }
    myArmy.forEach(soldier => soldier.getInfo())
    console.log('KILL ALL')
    const [onOffWhiteWalker] = myArmy
    onOffWhiteWalker.getFatherInstance().kill()
    myArmy.forEach(soldier => soldier.getInfo())
    console.log('RESURRECT ALL')
    onOffWhiteWalker.getFatherInstance().resurrect()
    myArmy.forEach(soldier => soldier.getInfo())
})()

用例


  • 当我们要避免创建大量对象时
  • 当我们要创建消耗大量内存的对象时
  • 当我们需要创建需要复杂计算的对象时
  • 当我们的资源有限时:计算能力,内存,空间等。

代理人


顾名思义,该代理模板用作外接程序的加载项或替代品,以控制对该对象的访问。

想象一下,塞塞王后发布了一项法令,禁止未经她的允许招募100多名士兵。我们如何实现呢?

我们将需要以下内容:

  • 阶级士兵
  • ArmyProxy类,用于过程控制
  • Cercei类的实例以获得许可

class Soldier {
    constructor(name) {
        this.name = name
    }
    attack() {
        console.log(`Soldier ${this.name} attack`)
    }
}

class Queen {
    constructor(name) {
        this.name = name
    }
    getConsent(casualNumber) {
        return casualNumber %2 === 0
    }
}

class ArmyProxy {
    constructor() {
        this.army = []
        this.available = 0
        this.queen = new Queen('Cercei')
        this.getQueenConsent = this.getQueenConsent.bind(this)
    }

    getQueenConsent() {
        return this.queen.getConsent(Math.floor(Math.random() * 200))
    }

    enrollSoldier() {
        if(!this.available) {
            const consent = this.getQueenConsent()
            if(!consent) {
                console.error(`The queen ${this.queen.name} deny the consent`)
                return
            }
            this.available = 100
        }
        this.army.push(new Soldier(this.army.length))
        this.available--
    }
}

(() => {
    const myArmy = new ArmyProxy()
    for(let i = 0; i < 1000; i++) {
        myArmy.enrollSoldier()
    }
    console.log(`I have ${myArmy.army.length} soldiers`)
})()

用例


  • 当我们要使用的对象距离很远(深层嵌套)并将逻辑保存在代理中时,以免影响客户端
  • 当我们希望在预期实际结果时给出近似结果时,其计算需要大量时间
  • 当我们想控制对象的访问或创建而又不干扰其逻辑时

Github 代码

注意 trans:这是一段有关设计模式的精彩视频

感谢您的关注。

All Articles