• 责任链模式


    职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。职责链模式的名字非常形象,一系列可能会处理请求的对象被连接成一条链,请求在这些对象之间依次传递,直到遇到一个可以处理它的对象,我们把这些对象称为链中的节点

    我负责品牌广告词包的录入,我需要对用户在标签输入框的输入进行处理,词包有很多种类,校验规则也不同:

    • 品牌关键词:长度不能超过8个字符,不能重复,不能包含符号
    • 长尾关键词:长度在8-30个字符之间,不能重复,不能包含符号
    • 问答性关键词:长度在8-30个字符之间,不能重复,结尾可以是英文问号,不能包含其他符号

    如果使用普通函数和大量的if-else语句来实现的校验逻辑:

    import { v4 as uuidv4 } from 'uuid';
    
    interface ValidationResult {
      id: string;
      name: string;
      error?: string;
    }
    
    function validateKeywordLength(keyword: string, type: 'brand' | 'longTail' | 'qnA'): string | null {
      if (type === 'brand' && (keyword.length <= 0 || keyword.length > 8)) {
        return `Length should be between 1 and 8 characters`;
      }
    
      if ((type === 'longTail' || type === 'qnA') && (keyword.length < 8 || keyword.length > 30)) {
        return `Length should be between 8 and 30 characters`;
      }
    
      return null;
    }
    
    function checkDuplicate(keyword: string, keywords: string[]): string | null {
      const occurrences = keywords.filter(item => item === keyword).length;
      if (occurrences > 1) {
        return 'Keyword is duplicated';
      }
      return null;
    }
    
    function validateSymbols(keyword: string, type: 'brand' | 'longTail' | 'qnA'): string | null {
      const hasSymbol = /[!@#$%^&*(),.:{}|<>]/.test(keyword);
      if (type !== 'qnA' && hasSymbol) {
        return 'Contains illegal symbols';
      }
    
      if (type === 'qnA') {
        if (keyword.endsWith('?')) {
          if (keyword.indexOf('?') !== keyword.lastIndexOf('?')) {
            return 'Multiple question marks found';
          }
        } else if (hasSymbol) {
          return 'Contains illegal symbols';
        }
      }
    
      return null;
    }
    
    function validateKeyword(keyword: string, type: 'brand' | 'longTail' | 'qnA', keywords: string[]): ValidationResult {
      const result: ValidationResult = {
        id: uuidv4(),
        name: keyword,
      };
    
      let error = validateKeywordLength(keyword, type);
      if (!error) {
        error = checkDuplicate(keyword, keywords);
      }
      if (!error) {
        error = validateSymbols(keyword, type);
      }
    
      if (error) {
        result.error = error;
      }
    
      return result;
    }
    
    function analyzeKeywords(input: string, type: 'brand' | 'longTail' | 'qnA'): ValidationResult[] {
      const keywords = input.split(',');
      const results: ValidationResult[] = keywords.map(keyword => validateKeyword(keyword, type, keywords));
      return results;
    }
    
    // 使用示例
    const input = "apple,orange,grape?,cherry!!,apple";
    const results = analyzeKeywords(input, 'qnA');
    console.log(results);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    这种方法使用了大量的if-else语句,并且逻辑较为直接,但可扩展性较差,并且难于维护。当需要添加新的校验规则或修改现有的校验逻辑时,可能需要修改多处代码。

    使用责任链模式

    下面是代码的详细注释:

    // 引入 uuid 库中的 v4 函数,为了生成唯一ID
    //import { v4 as uuidv4 } from 'uuid';
    
    // 定义校验结果的接口
    interface ValidationResult {
      id: string;  // 唯一ID
      name: string;  // 输入的关键词
      error?: string;  // 可选的错误消息
    }
    
    // 抽象的校验器类,定义了责任链模式的基本结构
    abstract class ValidatorChain {
      nextValidator?: ValidatorChain;  // 指向下一个校验器的引用
    
      // 设置下一个校验器并返回它
      setNext(validator: ValidatorChain): ValidatorChain {
        this.nextValidator = validator;
        return validator;
      }
    
      // 默认的校验方法,如果有下一个校验器则调用,否则返回基础结果
      validate(item: string, array: string[]): ValidationResult {
        return this.nextValidator?.validate(item, array) || { id: uuidv4(), name: item };
      }
    }
    
    // 长度校验器
    class LengthValidator extends ValidatorChain {
      maxLength: number;  // 允许的最大长度
      minLength: number;  // 允许的最小长度
    
      constructor(minLength: number, maxLength: number) {
        super();  // 调用父类的构造函数
        this.minLength = minLength;
        this.maxLength = maxLength;
      }
    
      // 对输入项进行长度校验
      validate(item: string, array: string[]): ValidationResult {
        if (item.length < this.minLength || item.length > this.maxLength) {
          return {
            id: uuidv4(),
            name: item,
            error: `Length should be between ${this.minLength} and ${this.maxLength} characters`
          };
        }
        // 如果长度符合要求,继续下一个校验
        return super.validate(item, array);
      }
    }
    
    // 重复校验器
    class DuplicateValidator extends ValidatorChain {
      validate(item: string, array: string[]): ValidationResult {
        // 判断是否有重复
        const isDuplicate = array.indexOf(item) !== array.lastIndexOf(item);
        if (isDuplicate) {
          return {
            //id: uuidv4(),
            name: item,
            error: 'Keyword is duplicated'
          };
        }
        // 如果没有重复,继续下一个校验
        return super.validate(item, array);
      }
    }
    
    // 符号校验器
    class SymbolValidator extends ValidatorChain {
      validate(item: string, array: string[]): ValidationResult {
        // 检查是否含有不允许的符号
        const hasSymbol = /[!@#$%^&*(),.:{}|<>]/.test(item);
        if (hasSymbol) {
          return {
            //id: uuidv4(),
            name: item,
            error: 'Contains illegal symbols'
          };
        }
        // 如果没有含有禁止的符号,继续下一个校验
        return super.validate(item, array);
      }
    }
    
    // 问答性关键词的符号校验器(对 ? 的处理有所不同)
    class QnASymbolValidator extends SymbolValidator {
      validate(item: string, array: string[]): ValidationResult {
        // 如果关键词以 ? 结尾,则继续下一个校验
        if (item.endsWith('?')) {
          return super.validate(item, array);
        }
    
        // 检查是否含有除 ? 以外的不允许的符号
        const hasOtherSymbols = /[^?][!@#$%^&*(),.:{}|<>]/.test(item);
        if (hasOtherSymbols) {
          return {
            //id: uuidv4(),
            name: item,
            error: 'Contains illegal symbols or multiple question marks'
          };
        }
        // 如果符号符合要求,继续下一个校验
        return super.validate(item, array);
      }
    }
    
    // 创建针对各种关键词类型的责任链
    const brandKeywordValidator = new LengthValidator(0, 8)
      .setNext(new DuplicateValidator())
      .setNext(new SymbolValidator());
    
    const longTailKeywordValidator = new LengthValidator(8, 30)
      .setNext(new DuplicateValidator())
      .setNext(new SymbolValidator());
    
    const qnAKeywordValidator = new LengthValidator(8, 30)
      .setNext(new DuplicateValidator())
      .setNext(new QnASymbolValidator());
    
    // 根据用户输入的关键词类型来执行相应的责任链校验
    function analyzeKeywords(input: string, type: 'brand' | 'longTail' | 'qnA'): ValidationResult[] {
      const keywords = input.split(',');  // 将输入按逗号分割
    
      let validator: ValidatorChain;
      if (type === 'brand') {
        validator = brandKeywordValidator;
      } else if (type === 'longTail') {
        validator = longTailKeywordValidator;
      } else if (type === 'qnA') {
        validator = qnAKeywordValidator;
      } else {
        throw new Error('Invalid keyword type');  // 如果类型不匹配,抛出错误
      }
    
      // 对每个关键词执行责任链校验
      const results: ValidationResult[] = keywords.map((keyword) => validator.validate(keyword, keywords));
      return results;
    }
    
    // 使用示例
    const input = "apple,orange,grape?,cherry!!";
    const results = analyzeKeywords(input, 'qnA');
    console.log(results);  // 输出校验结果
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144

    好的,这里是带有详细注释的完整代码:

    import { v4 as uuidv4 } from 'uuid';
    
    // 定义校验结果的接口
    interface ValidationResult {
      id: string;
      name: string;
      error?: string;  // 用于存储错误信息的字段
    }
    
    // 定义基础的校验器抽象类
    abstract class ValidatorChain {
      nextValidator?: ValidatorChain;
    
      // 设置下一个校验器
      setNext(validator: ValidatorChain): ValidatorChain {
        this.nextValidator = validator;
        return validator;
      }
    
      // 校验方法,子类需要根据具体的校验逻辑进行重写
      validate(item: string, type: 'brand' | 'longTail' | 'qnA', array: string[]): ValidationResult {
        return this.nextValidator?.validate(item, type, array) || { id: uuidv4(), name: item };
      }
    }
    
    // 定义长度校验器
    class LengthValidator extends ValidatorChain {
      maxLength: number;
      minLength: number;
      applicableTypes: ('brand' | 'longTail' | 'qnA')[];
    
      // 接收最小长度、最大长度和适用的关键词类型作为参数
      constructor(minLength: number, maxLength: number, applicableTypes: ('brand' | 'longTail' | 'qnA')[]) {
        super();
        this.minLength = minLength;
        this.maxLength = maxLength;
        this.applicableTypes = applicableTypes;
      }
    
      validate(item: string, type: 'brand' | 'longTail' | 'qnA', array: string[]): ValidationResult {
        // 判断当前关键词类型是否适用于此校验器
        if (!this.applicableTypes.includes(type)) {
          return super.validate(item, type, array);
        }
    
        if (item.length < this.minLength || item.length > this.maxLength) {
          return {
            id: uuidv4(),
            name: item,
            error: `Length should be between ${this.minLength} and ${this.maxLength} characters`
          };
        }
        return super.validate(item, type, array);
      }
    }
    
    // 定义重复校验器
    class DuplicateValidator extends ValidatorChain {
      applicableTypes: ('brand' | 'longTail' | 'qnA')[];
    
      constructor(applicableTypes: ('brand' | 'longTail' | 'qnA')[]) {
        super();
        this.applicableTypes = applicableTypes;
      }
    
      validate(item: string, type: 'brand' | 'longTail' | 'qnA', array: string[]): ValidationResult {
        if (!this.applicableTypes.includes(type)) {
          return super.validate(item, type, array);
        }
    
        const isDuplicate = array.indexOf(item) !== array.lastIndexOf(item);
        if (isDuplicate) {
          return {
            id: uuidv4(),
            name: item,
            error: 'Keyword is duplicated'
          };
        }
        return super.validate(item, type, array);
      }
    }
    
    // 定义符号校验器
    class SymbolValidator extends ValidatorChain {
      applicableTypes: ('brand' | 'longTail' | 'qnA')[];
    
      constructor(applicableTypes: ('brand' | 'longTail' | 'qnA')[]) {
        super();
        this.applicableTypes = applicableTypes;
      }
    
      validate(item: string, type: 'brand' | 'longTail' | 'qnA', array: string[]): ValidationResult {
        if (!this.applicableTypes.includes(type)) {
          return super.validate(item, type, array);
        }
    
        const hasSymbol = /[!@#$%^&*(),.:{}|<>]/.test(item);
        if (hasSymbol) {
          return {
            id: uuidv4(),
            name: item,
            error: 'Contains illegal symbols'
          };
        }
        return super.validate(item, type, array);
      }
    }
    
    // 定义问答关键词的符号校验器,继承自SymbolValidator
    class QnASymbolValidator extends SymbolValidator {
      validate(item: string, type: 'brand' | 'longTail' | 'qnA', array: string[]): ValidationResult {
        if (item.endsWith('?')) {
          return super.validate(item, type, array);
        }
    
        const hasOtherSymbols = /[^?][!@#$%^&*(),.:{}|<>]/.test(item);
        if (hasOtherSymbols) {
          return {
            id: uuidv4(),
            name: item,
            error: 'Contains illegal symbols or multiple question marks'
          };
        }
        return super.validate(item, type, array);
      }
    }
    
    // 创建一个通用的责任链
    const keywordValidator = new LengthValidator(0, 8, ['brand'])
      .setNext(new LengthValidator(8, 30, ['longTail', 'qnA']))
      .setNext(new DuplicateValidator(['brand', 'longTail', 'qnA']))
      .setNext(new SymbolValidator(['brand', 'longTail']))
      .setNext(new QnASymbolValidator(['qnA']));
    
    // 根据关键词类型执行单一责任链的校验
    function analyzeKeywords(input: string, type: 'brand' | 'longTail' | 'qnA'): ValidationResult[] {
      const keywords = input.split(',');
      const results: ValidationResult[] = keywords.map((keyword) => keywordValidator.validate(keyword, type, keywords));
      return results;
    }
    
    // 使用示例
    const input = "apple,orange,grape?,cherry!!";
    const results = analyzeKeywords(input, 'qnA');
    
    
    console.log(results);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147

    这段代码展示了如何创建一个通用的责任链,根据关键词的类型决定是否执行校验。通过对校验器的适用关键词类型进行配置,可以轻松地对责任链进行扩展和修改。

    "哪种方式更好"取决于具体的应用场景和需求。下面我们对两种方式进行比较:

    三条责任链的优势

    1. 明确性:每条链有明确的责任和用途,对于开发者而言,更容易理解每条链的目的。
    2. 独立性:各自的链可以独立地进行修改和扩展,不会对其他链产生影响。
    3. 可读性:当一个特定类型的关键词需要经过的验证规则较多时,将它们组织在单独的责任链中可能更有可读性。

    单条责任链的优势

    1. 灵活性:在一个统一的链中,可以轻松地为各种关键词类型配置和修改验证规则。
    2. 维护:如果多种关键词类型有共同的验证规则,那么只需在单一的责任链中进行修改,而不需要在每条链上进行重复的修改。
    3. 扩展性:增加新的关键词类型或新的验证规则时,只需修改一条链。

    选择的建议

    • 如果每种关键词类型的验证规则都相对独立,并且未来不太可能共享验证逻辑,那么采用三条责任链可能更有优势。
    • 如果多种关键词类型有很多共同的验证规则,并且希望能够轻松地为各种类型的关键词配置验证规则,那么单条责任链可能更合适。
    • 如果考虑到未来的扩展性,或者验证规则会频繁变动,单条责任链的方式可能更为灵活和易于维护。
  • 相关阅读:
    新加坡暑假旅游攻略:一天玩转新加坡圣淘沙岛
    Python: RGBD转点云
    文件属性之文件权限
    网络要素服务(WFS)详解
    道路救援入驻派单小程序开源版开发
    计算机基础 - 原码,反码,补码
    【并发编程】第二章:从核心源码深入ReentrantLock锁
    Spark实现TopN
    HCIE-storage-lab 考试注意事项,实验考试细节及问题等
    【调试】ftrace(一)基本使用方法
  • 原文地址:https://blog.csdn.net/weixin_43850639/article/details/133870688