zoukankan      html  css  js  c++  java
  • ruby和javascript的策略模式

    来自官方

    策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

    结构图

    ruby中的简单实现(代码来自《ruby设计模式》)

    # 根据鸭子模型, 没有让策略对象继承一个提供统一接口的基类
    # 策略1
    class HTMLFormatter
      def output_report title, text
        puts '<html>'
        puts '    <head>'
        puts '        <title>' + title + '</title>'
        puts '    </head>'
        puts '    <body>'
        text.each do |line|
          puts "<p>#{line}</p>"
        end
        puts '    </body>'
        puts '</html>'
      end
    end
    
    # 策略2
    class PlainTextFormatter
      def output_report title, text
        puts '******** ' + title + ' ********'
        text.each do |line|
          puts line
        end
      end
    end
    
    # 这边之所以用策略模式来做,是因为Reporter有可能会将text打印成不同格式的文本,如果将来需要
    # 支持xml那么只需要增加一个支持output_report接口的类就可以了
    class Reporter
      attr_reader :title, :text
      attr_accessor :formater
    
      def initialize formater
        @title = 'My Report'
        @text = ['This is my report', 'Please see the report', 'It is ok']
        @formater = formater
      end
    
      # 策略对象具有共同的接口
      def output_report
        @formater.output_report @title, @text
      end
    end
    
    # 环境对象调用不同的策略
    Reporter.new(HTMLFormatter.new).output_report
    Reporter.new(PlainTextFormatter.new).output_report
    

    那么策略模式到底可以在什么时候用呢?
    我们先来一个例子,一般情况下,如果我们要做数据合法性验证,很多时候都是按照swith语句来判断,但是这就带来几个问题,首先如果增加需求的话,我们还要再次修改这段代码以增加逻辑,而且在进行单元测试的时候也会越来越复杂,代码如下:

    # 在最早的时候我差不多就是用这样的方式去做表单的数据验证的
    validator = {
                validate: function (value, type) {
                    switch (type) {
                        case 'isNonEmpty ':
                            {
                                return true; // NonEmpty 验证结果
                            }
                        case 'isNumber ':
                            {
                                return true; // Number 验证结果
                                break;
                            }
                        case 'isAlphaNum ':
                            {
                                return true; // AlphaNum 验证结果
                            }
                        default:
                            {
                                return true;
                            }
                    }
                }
            };
            //  测试
            alert(validator.validate("123", "isNonEmpty"));
    

    ok, 用策略模式去改造一下, 首先分析一下validator是一个策略环境,不同的验证就是一个策略对象,我们需要做的就是让不同的策略对象支持统一的接口
    重构代码如下:

    var validator = {
    
        types: {},
    
        messages: [],
    
        config: {},
    
        validate: function (data) {
    
            var i, msg, type, checker, result_ok;
    
            this.messages = [];
    
            for (i in data) {
                if (data.hasOwnProperty(i)) {
    
                    type = this.config[i];  // 根据key查询是否有存在的验证规则
    
                    checker = this.types[type]; // 获取验证规则的验证类
    
                    if (!type) {
                        continue; // 如果验证规则不存在,则不处理
                    }
                    if (!checker) { // 如果验证规则类不存在,抛出异常
                        throw {
                            name: "ValidationError",
                            message: "No handler to validate type " + type
                        };
                    }
                    # 开始执行按需执行不同的策略
                    result_ok = checker.validate(data[i]); // 使用查到到的单个验证类进行验证
                    if (!result_ok) {
                        msg = "Invalid value for *" + i + "*, " + checker.instructions;
                        this.messages.push(msg);
                    }
                }
            }
            return this.hasErrors();
        },
    
        hasErrors: function () {
            return this.messages.length !== 0;
        }
    };
    
    // 验证给定的值是否不为空
    validator.types.isNonEmpty = {
        validate: function (value) {
            return value !== "";
        },
        instructions: "传入的值不能为空"
    };
    
    // 验证给定的值是否是数字
    validator.types.isNumber = {
        validate: function (value) {
            return !isNaN(value);
        },
        instructions: "传入的值只能是合法的数字,例如:1, 3.14 or 2010"
    };
    
    // 验证给定的值是否只是字母或数字
    validator.types.isAlphaNum = {
        validate: function (value) {
            return !/[^a-z0-9]/i.test(value);
        },
        instructions: "传入的值只能保护字母和数字,不能包含特殊字符"
    };
    
    var data = {
        first_name: "Tom",
        last_name: "Xu",
        age: "unknown",
        username: "TomXu"
    };
    
    validator.config = {
        first_name: 'isNonEmpty',
        age: 'isNumber',
        username: 'isAlphaNum'
    };
    
    validator.validate(data);
    
    if (validator.hasErrors()) {
        console.log(validator.messages.join("
    "));
    }
    

    早期的时候看到这样的代码我只想说fuck, 明明很简单的东西,非搞的这么复杂有必要么?
    当然, 假设项目很简单,用重构前的代码OK, 而且这里也只是去举个例子

    那么同样的需求用ruby如何实现呢?
    代码如下:(这里我完全是用javascript的思维来写ruby了, 不知道阅读性有没有问题)

    # coding: utf-8
    
    # 策略1
    class IsNonEmpty
      def self.check(data)
        data.nil?
      end
    
      def self.notice
        "传入的值不能为空
    "
      end
    end
    
    # 策略2
    class IsAlphaNum
      def self.check(data)
        data =~ /[^a-z0-9]/
      end
    
      def self.notice
        "传入的值只能保护字母和数字,不能包含特殊字符
    "
      end
    end
    
    # 环境
    class Validate
      attr_reader :msg
    
      def initialize
        # @types = []
        @msg = ''
        # @config = {}
      end
    
      # 这个地方没有再按照javascript的逻辑去写,这边如果需要不同的实例支持不同的验证的话,
      # 可以将策略对象添加到@types中
      # @config 同理
      # def add_types(type)
      #   @types << type
      # end
    
      def check(hash_data)
        hash_data.each_pair do |key, val|
          @msg << "#{key}errors #{val[:type].notice}" if val[:type].check(val[:data])
        end
      end
    
      def errors
        !@msg.nil?
      end
    end
    
    validate = Validate.new
    
    hash_data = {
        first: {type: IsNonEmpty, data: 1324},
        second: {type: IsNonEmpty, data: nil},
        third: {type: IsAlphaNum, data: '123angel'},
        forth: {type: IsAlphaNum, data: '123sf@/!'}
    }
    
    validate.check(hash_data)
    if validate.errors
      puts validate.msg
    end
    

    最后: 本打算总结出ruby和javascript共享接口的不同, 奈何能力有限,只能意会出来却不能言传出来
    也可能是为没有理解出其中的关键点

  • 相关阅读:
    深度通道编程模型
    缓冲区溢出实验实验楼
    团队作业(一)
    信安导论学习阶段性总结
    Python总结
    mysql下载及安装
    初识python
    MobileMenuScene来点动画效果
    移动菜单页面PlayerInput
    渲染和填充MobileMenuListItem
  • 原文地址:https://www.cnblogs.com/angelfan/p/4389879.html
Copyright © 2011-2022 走看看