策略模式(Strategy Pattern)

99 阅读2分钟

相关链接: 策略模式 | Shawkry's blog

定义一系列算法,把他们一个一个封装起来,并且使他们可以相互替换。

优点

  1. 符合开闭原则:无需修改上下文即可添加新策略,更容易理解和拓展
  2. 算法复用:不同上下文可以共享策略对象,避免重复
  3. 代码优雅:避免大量的 if-else/switch-case 判断

解决场景

  1. 不需要使用者创造它的实例对象,直接供使用者调用
  2. 需要用一些行为进行限制,比如全局只需要一个实例(消息弹窗、购物车等)

示例

表单校验

interface IValidateReturn  {
    isValid: boolean;
    error?: string;
}

interface IValidationRule {
    validate(value: string): IValidateReturn;
}

// 具体策略实现,校验邮箱
const emailRule: IValidationRule = {
    validate: (v) => ({
        isValid: /^[\w-]+@[\w-]+\.\w+$/.test(v),
        error: '邮箱格式不正确'
    })
};

// 具体策略实现,校验手机号码
const phoneNumberRule: IValidationRule = {
    validate: (v) => ({
        isValid: /^1[3-9]\d{9}$/.test(v),
        error: '手机号格式不正确'
    })
};

// 具体策略实现,必填校验规则
const requiredRule: IValidationRule = {
    validate: (v) => ({
        isValid: v.trim().length > 0,
        error: '该字段不能为空'
    })
};

class SmartValidator {
    private _rules: IValidationRule[] = [];

    public constructor(rules: IValidationRule[] = []) {
        this._rules = rules;
    }

    // 设置规则
    public setRules(rules: IValidationRule[]): void {
        this._rules = rules;
    }

    // 执行验证(返回第一个错误)
    public validate(value: string): IValidateReturn {
        for (const rule of this._rules) {
            const result = rule.validate(value);
            if (!result.isValid) {
                return result;
            }
        }
        return { isValid: true };
    }
}

const validator = new SmartValidator();

// 根据表单类型设置不同的验证规则
const formType = 'email'; // 可以是 'email' 或 'phone'

if (formType === 'email') {
    validator.setRules([requiredRule, emailRule]);
}
else {
    validator.setRules([requiredRule, phoneNumberRule]);
}

// 测试空值
console.log('空值验证:', validator.validate(''));
// 测试有效值
console.log('格式验证:', validator.validate('test@example.com'));

与if-else和switch-case对比

使用 if-else 判断实现:

type Role = 'user' | 'admin' | 'vip';
function getFeaturesIfElse(role: Role): string[] {
    if (role === 'user') {
        return ['dashboard', 'basic-profile'];
    } else if (role === 'admin') {
        return ['dashboard', 'user-management', 'system-settings'];
    } else if (role === 'vip') {
        return ['dashboard', 'premium-content', 'advanced-settings'];
    } else {
        return [];
    }
}

const featuresIfElse1 = getFeaturesIfElse('admin');
const featuresIfElse2 = getFeaturesIfElse('vip');

使用 switch-case 实现:

function getFeaturesSwitchCase(role: Role): string[] {
  switch (role) {
    case 'user':
      return ['dashboard', 'basic-profile'];
    case 'admin':
      return ['dashboard', 'user-management', 'system-settings'];
    case 'vip':
      return ['dashboard', 'premium-content', 'advanced-settings'];
    default:
      return [];
  }
}

const featuresSwitchCase1 = getFeaturesSwitchCase('admin');
const featuresSwitchCase2 = getFeaturesSwitchCase('vip');

使用策略模式实现:

type Role = 'user' | 'admin' | 'vip';

const FeatureStrategies = {
    user: () => ['dashboard', 'basic-profile'],
    admin: () => ['dashboard', 'user-management', 'system-settings'],
    vip: () => ['dashboard', 'premium-content', 'advanced-settings']
};

function getFeatures(role: Role): string[] {
    return FeatureStrategies[role]();
}

const features1 = getFeatures('admin');
const features2 = getFeatures('vip');