从 JavaScript 看策略模式

261 阅读3分钟

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。

前言

策略模式是很简单实用的模式,在之前讲面向对象的原则的时候其实就已经体现出来了。策略模式是对多种功能的封装,通过一个中间控制器,统一的调用接口来实现。话不多说开始吧。

简单工厂

在实现策略模式之前,先来看一下简单工厂。他和策略模式类似,都是对一些功能进行了封装,而简单工厂是一种快速实现的封装,是比较基本的应用。比如我们要实现一个简单计算器的功能。

    function factory(condition) {
        swtich(condition) {
            case '+':
                return (a, b) => a + b
            case '-':
                return (a, b) => a - b
            case '*':
                return (a, b) => a * b
            case '/':
                return (a, b) => a - b
        }
    }
    
    const add = factory('+')
    add(1, 2)

我们对加减乘除的函数进行了封装,封装成一个简单的工厂,通过传入参数决定使用哪种方法。这种其实写成一个对象会更好。通过 obj['+'] 的方式拿到方法。或者把计算过程也封装进来,直接返回计算后的结果,也是可以的。但我们可以看到只要新增计算器的方法就一定会改这个工厂,然后给里面增加条件。这首先就不符合开闭原则。当这个计算器越来越大的时候,代码就会很复杂。如果不小心动了其他地方的代码那将是恶梦。而且如果我们想从里面再得到一个青春版的计算器就不方便实现了。

简而言之就是简单工厂是为了快速满足简单的需求的。

抽象工厂

抽象工厂的实现思路就是提供一些统一的方法名,然后用一个变量来代替某个具体名字的函数。

就比如 vue 的 renderer,提供了 ssr 和 dom 两种 renderer,通过指定用哪个 renderer 来决定后续的行为。与之类似的思路是:

    function ssr() {}
    
    function dom() {}
    
    function Renderer(render) {
        this.render = render
        // ...
    }
    
    const render = new Renderer(dom)
    
    render.xxx()

普通工厂和抽象工厂的区别在哪?简单工厂里面是条件分支语句,通过传入条件来返回对应行为。抽象工厂则是弱化了这个条件分支语句,直接传入基础方法,返回封装好的更多功能的方法,而且是改变内部的逻辑,不改变外部的使用方法。

策略模式

我们的策略模式可以结合抽象工厂进行实现。

我们依然用计算器作为例子,完成后可以与简单工厂对比一下实现的计算器有什么优点。

    function add(a, b) {
        return a + b
    }
    
    function minus(a, b) {
        return a - b
    }
    
    // 其他计算方法...
    
    class Calculator {
        constructor(method) {
            this.setMethod(method)
        }
        
        setMethod() {
            this.method = method
        }
        
        compute(a, b) {
            this.method(a, b)
        }
    }
    
    const calculator = new Calculator(add)
    
    calculator.compute(1, 2)

优点:我们只需要在初始化的时候确定一个方法 add,之后就可以以统一的形式来进行操作。而且我们不再需要改动以前写过的函数。只需要改变确定的方法即可。