策略模式

策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换。

其实就好比USB接口,鼠标、键盘就是算法,插上什么设备就拥有了什么功能。

对于前端来说最常见的就是表单验证逻辑了,把各种各样的验证逻辑封装成策略对象,然后输入框需要什么验证方法,给他安上就好了。

##策略模式的结构

封装类:也叫上下文,对策略进行二次封装 ,目的是避免高层模块对策略的直接调用。 抽象策略:通常情况下为一个接口,当各个实现类中存在着重复的逻辑时,则使用抽象类来封装这部分公共的代码,此时,策略模式看上去更像是模版方法模式。 具体策略:具体策略角色通常由一组封装了算法的类来担任,这些类之间可以根据需要自由替换。

##例子 下面使用Typescript实现的例子:

interface IStrategy {
    doSomething(): void;
}
class ConcreteStrategy1 implements IStrategy {
    public doSomething() {
        console.log("具体策略1");
    }
}
class ConcreteStrategy2 implements IStrategy {
    public doSomething() {
        console.log("具体策略2");
    }
}
class Context {
    private strategy: IStrategy;
    public execute() {
        this.strategy.doSomething();
    }
    constructor(strategy: IStrategy) {
        this.strategy = strategy;
    }
}

let context = new Context(new ConcreteStrategy1());
context.execute();

其实策略模式在原生js中更简单,因为函数也是对象,所以我们直接把策略定义为对象:

const strategies = {
    "ConcreteStrategy1" = function() {
        console.log("具体策略1");
    },
    "ConcreteStrategy2" = function() {
        console.log("具体策略2");
    },
}

const context = function(ConcreteStrategy){
    return strategies[ConcreteStrategy]();
}

context(ConcreteStrategy1) //"具体策略1"

##优点:

  • 策略类之间可以自由切换,由于策略类实现自同一个抽象,所以他们之间可以自由切换。
  • 易于扩展和复用,增加一个新的策略对策略模式来说非常容易,而且封装的算法还可以用在其它的地方。
  • 避免使用多重条件判断来决定使用哪一种算法,更易于维护。

##缺点:

  • 必须对客户端(调用者)暴露所有的策略类,因为使用哪种策略是由客户端来决定的,因此,客户端应该知道有什么策略,并且了解各种策略之间的区别,否则,后果很严重。例如,有一个排序算法的策略模式,提供了快速排序、冒泡排序、选择排序这三种算法,客户端在使用这些算法之前,是不是先要明白这三种算法的适用情况?再比如,客户端要使用一个容器,有链表实现的,也有数组实现的,客户端是不是也要明白链表和数组有什么区别?就这一点来说是有悖于迪米特法则(最少知识原则)的。

参考文献: 策略模式

Licensed under CC BY-NC-SA 4.0