zoukankan      html  css  js  c++  java
  • JS设计模式-策略模式

    参考:https://www.cnblogs.com/chenwenhao/p/12354176.html

    策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

    设计模式是针对某一类问题的优雅解决方案。策略模式出现的场景是,调用者要根据当前不同条件判断选择执行不同的策略,而这些策略是对某一问题的不同处理方案。

    初级的编码是,在策略调用方法中,根据不同的条件判断使用if...else等分支控制语句,在if代码块中书写策略的具体内容。如果业务需求很简单,只有两种解决方案,而且实现算法简单,这样写是没有问题的。

    如果需要判断的策略超过3个,甚至十几种、几十种,调用方法中条件判断就显得太多,不利于阅读和维护。此时就需要考虑使用策略模式进行重构了。

    重新分析问题场景,可以抽象出三个东西,策略调用、策略分发、策略定义。有了这个思想,不管代码如何实现,都是策略模式。


     下来通过代码,一步步讨论代码实现:

    先看下很多博客都会引用的栗子

    var calculateBonus = function( performanceLevel, salary ){
    ​    if ( performanceLevel === 'A' ){
    ​        return salary * 4;
    ​    }
    
    ​    if ( performanceLevel === 'B' ){
    ​        return salary * 3;
    ​    }
    
    ​    if ( performanceLevel === 'C' ){
    ​        return salary * 2;
    ​    }
    };
    
    calculateBonus( 'B', 20000  );        // 输出:40000
    
    calculateBonus( 'S', 6000 );        // 输出:24000

    可以看出,我们提到的策略调用、策略分发、策略定义都在一个代码块中,随着业务逻辑的增加、算法复杂度的增加,函数会变得越来越庞大,难以维护,违反开放-封闭原则。


     接下来,我们对它进行重构:

    var performanceA = function( salary ){
    ​    return salary * 4;
    };
    
    var performanceB = function( salary ){
    ​    return salary * 3;
    };
    
    var performanceC = function( salary ){
    ​    return salary * 2;
    };
    
    var calculateBonus = function( performanceLevel, salary ){
    ​    if ( performanceLevel === 'A' ){
    ​        return performanceA( salary );
    ​    }
    
    ​    if ( performanceLevel === 'B' ){
    ​        return performanceB( salary );
    ​    }
    
    ​    if ( performanceLevel === 'C' ){
    ​        return performanceC( salary );
    ​    }
    };
    
    calculateBonus(  'A' , 10000 );    // 输出:40000

    可以看出,虽然将策略定义分离出去了,但是策略调用和策略分发还在一个函数中,函数还还是存在越来越庞大的风险。


    进一步重构:

    ①  策略定义

    var performanceA = function() {};
    performanceA.prototype.calculate = function (salary) {
    ​    return salary * 4
    }
    
    var performanceB = function(){};
    performanceB.prototype.calculate = function( salary ){
    ​    return salary * 3;
    };
    
    var performanceC = function(){};
    performanceC.prototype.calculate = function( salary ){
    ​    return salary * 2;
    };

    ② 策略分发

    var Bonus = function(){
    ​    this.salary = null;      // 原始工资this.strategy = null;    // 绩效等级对应的策略对象
    };
    
    Bonus.prototype.setSalary = function( salary ){
    ​    this.salary = salary;    // 设置员工的原始工资
    };
    
    Bonus.prototype.setStrategy = function( strategy ){
    ​    this.strategy = strategy;    // 设置员工绩效等级对应的策略对象
    };
    
    Bonus.prototype.getBonus = function(){    //取得奖金数额return this.strategy.calculate( this.salary );    // 把计算奖金的操作委托给对应的策略对象
    };

    ③ 策略调用

    var bonus = new Bonus();
    bonus.setSalary( 10000 );
    
    bonus.setStrategy( new performanceA() );  //设置策略对象
    console.log( bonus.getBonus() );    // 输出:40000    
    
    bonus.setStrategy( new performanceB() );  //设置策略对象
    console.log( bonus.getBonus() );    // 输出:30000  

     可以看出,将整个过程分成了三部分,需要增加策略时,在策略定义中添加,不需要修改之前的代码;策略分发也不需要改动;调用时需要指定具体的策略。

     这此重构已经符合了我们提到的策略调用、策略分发、策略定义,是一个完整的,有扩展性,符合开放封闭原则的代码结构。


     其实,思想是一样的,但具体代码还可以采用不同形式:

    ① 利用js对象来组织策略定义代码

    var strategies = {
    ​    "A": function( salary ){
    ​        return salary * 4;
    ​    },
    
    ​    "B": function( salary ){
    ​        return salary * 3;
    ​    },
    
    ​    "C": function( salary ){
    ​        return salary * 2;
    ​    }
    };

    ② 利用js函数传参 来组织策略分发代码

    var calculateBonus = function( level, salary ){
    ​    return strategies[level](salary)
    };
    
    console.log( calculateBonus( 'A', 20000 ) );        // 输出: 80000
    console.log( calculateBonus( 'B', 10000 ) );        // 输出: 30000

    万变不离其宗,只要实现了使用、分发、定义的分离,采用何种数据结构组织代码都是ok的。


     我实际vue项目开发中遇到的,策略模式除了对这种纯js逻辑的处理,对于同一个模板应用于多个业务的场景也非常适用。

    ① .vue文件 调用

     computed: {
        instance: function () {
          return new moduleFactory(this.moduleName, this.type)
        },
      },

    ② 策略定义

    class ModuleFactory {
      constructor(moduleName, type) {
        this.moduleName = moduleName;
        this.type = type;
        if (this.moduleName == 'themeClassify') {
          switch (this.type) {
            case 1:
              return new DomainGroup();
            case 2:
              return new Domain();
            case 3:
              return new Theme();
          }
        } else if (this.moduleName == 'customClassify') {
          switch (this.type) {
            case 1:
              return new Category();
            case 2:
              return new Item();
            case 3:
              return new Order();
            case 4:
              return new SubOrder();
          }
        }
      }
    }
    class ThemeClassify {
      constructor() {
        this.showAddBtn = true;
        this.showMoreInfo = true;
        this.groupCountClassName = 'group-count-3';
        this.svcName = 'layerManageSvc';
      }
      getQueryRequest(params, target) {
        return target.service.layerManageSvc.query(params)
      }
      getDelRequest(params, target) {
        return target.service.layerManageSvc.delete(params)
      }
    }
    class CustomClassify {
      constructor() {
        this.showAddBtn = true;
        this.showMoreInfo = false;
        this.groupCountClassName = 'group-count-4';
        this.svcName = 'layerManageSvc';
      }
      getQueryRequest(params, target) {
        return target.service.layerManageSvc.query(params)
      }
      getDelRequest(params, target) {
        return target.service.layerManageSvc.delete(params)
      }
    }
    class DomainGroup extends ThemeClassify {
      constructor() {
        super();
        this.title = '主题域分组';
      }
      getSaveRequest(oData, target) {
        return target.service.layerManageSvc.postDomainGroup(oData)
      }
    }
    class Domain extends ThemeClassify {
      constructor() {
        super();
        this.title = '主题域';
      }
      getSaveRequest(oData, target) {
        return target.service.layerManageSvc.postDomain(oData)
      }
    }
    class Theme extends ThemeClassify {
      constructor() {
        super();
        this.title = '主题';
        this.showAddBtn = false;
      }
      getSaveRequest(oData, target) {
        return target.service.layerManageSvc.postBusiness(oData)
      }
    }
    class Category extends CustomClassify {
      constructor() {
        super();
        this.title = '类';
      }
    }
    class Item extends CustomClassify {
      constructor() {
        super();
        this.title = '项';
      }
    }
    class Order extends CustomClassify {
      constructor() {
        super();
        this.title = '目';
      }
    }
    
    class SubOrder extends CustomClassify {
      constructor() {
        super();
        this.title = '细目';
        this.showAddBtn = false;
      }
    }
    export default ModuleFactory;

    可以看出,我的策略分发使用类的构造函数实现的,策略定义也是使用类定义的,而且对于相同的策略类使用了继承特性。

    至此,策略模式走完了他井猜的一生。

    --------学习 交流 提升-----------
  • 相关阅读:
    Ruby 2
    Ruby 1
    莱布尼兹:与牛顿争吵了一生的斗士 微积分的奠基人之一―莱布尼茨
    如何实现html页面自动刷新
    css z-index的层级关系
    让网页变灰的实现_网站蒙灰CSS样式总汇
    利用CSS变量实现炫酷的悬浮效果
    离线电商数仓(十四)之系统业务数据仓库数据采集(一)电商业务简介
    离线电商数仓(十三)之用户行为数据采集(十三)采集通道启动/停止脚本
    离线电商数仓(十)之用户行为数据采集(十)组件安装(六)采集日志Flume(二)消费Kafka数据Flume
  • 原文地址:https://www.cnblogs.com/blogNYGJ/p/14536462.html
Copyright © 2011-2022 走看看