• JAVA设计模式之策略模式


    1.策略模式的介绍

      策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。

    简单的说,策略模式代表了一类算法的通用解决方案,你可以在运行时选择使用哪种解决方案。

    策略模式的重心

      策略模式的重心不是如何实现算法, 而是如何组织、调用这些算法, 从而使得程序结构更加灵活,具有更好的维护性和扩展性。

    算法的平等性

      策略模式一个很大的特点就是各个策略算法的平等性。对于一系列具体的策略算法,地位都是一样的,因此可以实现算法之间可以互相替换。所有的策略算法在实现上也是相互独立的,相互之间是没有依赖的。所以可以这样描述这一系列策略算法:策略算法是相同行为的不同实现。

    运行时策略的唯一性

      运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个。

    公有的行为

      经常见到的是,所有的具体策略都有一些公有的行为。这时候,就应该把这些公有的行为放到共同的抽象策略角色

    Strategy类里面。这时不能使用接口,应该使用抽象类来实现。

    2.策略模式的结构

      策略模式包含三部分内容:(如图所示)

        a.一个或多个使用策略对象的客户.(环境角色)

        b.一个代表某个算法的接口, 它是策略模式的接口. (抽象策略角色)

        c.一个或多个该接口的具体实现, 它们代表了算法的多种实现.(具体策略角色)

     

    3.策略模式的应用

      a.容错恢复机制, 程序运行的时候, 如果发生某种错误, 系统并不会直接挂掉或者说影响系统的其他功能点. 而是系统可以容忍这样的错误, 并且事先提供好了这种容错恢复机制, 来使得程序正常的运行下去.

      例如: 一个系统要对所有的操作进行日志记录, 且需要把日志记录落库, 方便后续的使用, 但是在把日志记录落库的时候, 可能会发生错误, 如数据库出现问题, 那就先可以记录在文件里面, 等到数据库问题修复, 再把文件中的日志记录同步到数据库中去. 对于这样的功能设计, 可以采用策略设计模式, 根据需要在运行期间进行动态的切换.

      b.假设现在要设计一个会员机制的购物系统, 对本系统的所有SVIP提供打八折的购物优惠, 对本系统的所有VIP提供打九折的购物优惠, 对非会员购物不打折. 那么对于这样的系统功能设计, 也可以采用策略模式来设计.

      c.使用不同的条件(物品的重量或者颜色等)来筛选库存中的物品, 可以将这一模式应用到更广泛的领域, 比如使用不同的标准来验证输入的有效性, 使用不同的方式来分析或者格式化输入.

    4.策略模式Demo

      假设现在需要根据业务的需求,对调用接口传进来的参数,选择合适的策略进行处理,这里假设有策略一和策略二。

      Client:

     

     1 /**
     2  * @author lyh
     3  * @version v-1.0.0
     4  * @since 2021/6/2
     5  */
     6 public class Client {
     7     public static void main(String[] args) {
     8         //根据需要客户自行选择策略
     9 
    10         //选择策略1
    11         StrategyObj strategyOne = new StrategyObj(new StrategyOne());
    12         System.out.println(strategyOne.strategy("one"));
    13         //选择策略2
    14         StrategyObj strategyTwo = new StrategyObj(new StrategyTwo());
    15         System.out.println(strategyTwo.strategy("two"));
    16     }
    17 }
    18 
    19 输出:
    20 执行策略1!
    21 执行策略2!
    22 
    23 Process finished with exit code 0

      策略接口:

     1 /**
     2  * @desc:策略接口
     3  */
     4 public interface Strategy {
     5     String execute(String s);
     6 }
     7 
     8 /**
     9  * @desc:策略接口封装
    10  */
    11 public class StrategyObj {
    12 
    13     private final Strategy strategy;
    14 
    15     public StrategyObj(Strategy v) {
    16         this.strategy = v;
    17     }
    18 
    19     public String strategy(String s) {
    20         return strategy.execute(s);
    21     }
    22 
    23 }

      策略实现:

     1 /**
     2  * @desc:策略一
     3  */
     4 public class StrategyOne implements Strategy {
     5     @Override
     6     public String execute(String s) {
     7         return "执行策略1!";
     8     }
     9 }
    10 
    11 /**
    12  * @desc:策略二
    13  */
    14 public class StrategyTwo implements Strategy {
    15     @Override
    16     public String execute(String s) {
    17         return "执行策略2!";
    18     }
    19 }

    5.使用Lambda表达式

      通过上面的demo应该可以意识到Strategy是一个函数式接口;除此之外,它还与Predicate<String>具有同样的函数描述。这意味着我们不需要声明新的类来实现不同的策略,通过直接传递Lambda表达式就能达到同样的目的且更简洁。

    1 public class Client {
    2     public static void main(String[] args) {
    3         StrategyObj strategyOne = new StrategyObj((String s) -> {return "执行策略1";});
    4         System.out.println(strategyOne.strategy("one"));
    5 
    6         StrategyObj strategyTwo = new StrategyObj((String s) -> {return "执行策略2";});
    7         System.out.println(strategyTwo.strategy("two"));
    8     }
    9 }

      Lambda表达式避免了采用策略设计模板时僵化的模板代码。仔细看上面的代码会发现,Lambda表达式实际已经对策略进行了封装, 这就是创建策略设计模式的初衷.

    6.策略模式的优缺点

      优点

      a.使用策略模式可以避免使用多重条件if...else if...else语句, 多重条件不易维护且代码可读性差.

      b.策略模式提供了管理相关的算法族的办法. 策略类的等级结构定义了一个算法或者行为族. 恰当使用继承可以把公共的代码移到父类里面, 从而避免代码重复.

      缺点

      a.客户端必须知道所有的策略类, 并自行决定使用哪一个策略类. 这就意味着客户端必须理解这些算法的区别, 以便适时选择恰当的算法类. 换言之, 策略模式只适用于客户端知道算法或行为的情况.

      b.由于策略模式把每个具体的策略实现都单独封装成类, 如果备选的策略很多的话, 那么对象的数目就会很多.

     

     

  • 相关阅读:
    kubernetes安全机制
    Django 与 Vue交互跨域问题解决
    ELK收集Kubernetes平台日志
    minikube 安装
    k8s安装教程
    Go 语言Map(集合)
    Go 语言类型转换
    Go 语言递归函数
    今天学习:CSS中的类class和标识id选择符(.和#号) 第一季
    人工智能星际争霸2教程
  • 原文地址:https://www.cnblogs.com/lu97/p/14840729.html
Copyright © 2020-2023  润新知