设计模式:(Strategy Pattern)策略模式

184 阅读2分钟

如何在 TypeScript 中使用和实现策略模式来解决 Web 项目中的实际问题。

欢迎来到 TypeScript 中的设计模式系列,它介绍了使用TypeScript进行Web开发的一些有用的设计模式。

系列文章如下:

设计模式对 Web 开发人员来说非常重要,我们可以通过掌握它们来编写更好的代码。在本文中,我将使用TypeScript来介绍策略模式

注册和登录是 Web 应用程序中的重要功能。注册 Web 应用程序时,更常见的注册方式是使用账户/密码、电子邮件或手机。注册成功后,就可以使用相应的方法登录了。

function login(mode) {
  if (mode === 'account') {
    loginWithPassword();
  } else if (mode === 'email') {
    loginWithEmail();
  } else if (mode === 'mobile') {
    loginWithMobile();
  }
}

当需要支持更多的第三方平台登录方式时,我们需要修之前的login函数:

function login(mode) {
  if (mode === 'account') {
    loginWithPassword();
  } else if (mode === 'email') {
    loginWithEmail();
  } else if (mode === 'mobile') {
    loginWithMobile();
  } else if (mode === 'weixin') {
    loginWithWeixin();
  } else if (mode === 'juejin') {
    loginWithJuejin()
  }
}

如果以后我们继续添加或修改登录方式,我们会发现该login功能变得越来越难以维护。对于这个问题,我们可以使用策略模式将不同的登录方式封装成不同的登录策略。

为了更好的理解下面的代码,我们先看看对应的UML图:

iShot_2022-10-08_00.20.08.png

在上图中,我们定义了一个Strategy接口

interface Strategy {
    authenticate(args: any[]):boolean
}
// 第三方登录类
class JuejinStrategy implements Strategy {
    authenticate(args: any[]) {
        const [token] = args;
        if (token !== "password") {   
            return false;
        }
        return true;
    }
}


// 本地登录类
class LocalStrategy implements Strategy {
    authenticate(args: any[]) {
    const [username, password] = args;
    if (username !== "kevin" && password !== "password123") {  
        return false;
    }
    return true;
    }
}

有了不同的登录策略后,我们定义一个Authenticator类来在不同的的登录策略之间切换,并进行相应的认证操作

class Authenticator {
    strategies: Record<string, Strategy> = {}
    
    use(name:string, strategy:Strategy) {
        this.strategies[name] = strategy
    }
    
    authenticate(name:string, ...args:any) {
        if(!this.strategies[name]) {
          return false
        }
        
        return this.strategies[name].authenticate.apply(null, args)
    }

}

通过Authenticator 类来调用不用的登录方式

const auth = new Authenticator()
auth.use('local', new LocalStrategy())
auth.use('juejin', new JuejinStrategy())

function login(mode: string, ...args: any) {
  return auth.authenticate(mode, args);
}

login("juejin", "password123");
login("local", "kevin", "password123");

除了登录认证场景外,策略模式还可以用在表单字段验证场景中。它还可以用于优化 if else 分支过多的问题。

如果使用 Node.js 开发认证服务,可以看一下passport.js模块

最后总结一下策略模式的使用场景:

  • 当系统需要动态选择几种算法中的一种时,可以将每种算法封装成一个策略类。
  • 多个类只是行为不同,您可以使用策略模式在运行时动态选择要执行的具体行为。