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;

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

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

 

posted @ 2021-03-15 14:07  yuanxv  阅读(167)  评论(0编辑  收藏  举报