zoukankan      html  css  js  c++  java
  • 策略模式

    Define a family of algorithms,encapsulate each one,and make them interchangeable.
    定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。

    策略模式(Strategy Pattern) 也叫 政策模式(Policy Pattern)。指的是对象具备某个行为,但是在不同的场景中,该行为有不同的实现算法。比如一个人的交税比率与他的工资有关,不同的工资水平对应不同的税率。

    策略模式 使用的就是面向对象的继承和多态机制,从而实现同一行为在不同场景下具备不同实现。

    策略模式 本质:分离算法,选择实现

    主要解决

    在有多种算法相似的情况下,使用 if...else 或 switch...case 所带来的复杂性和臃肿性。

    优缺点

    优点

    • 算法多样性,且具备自由切换功能;
    • 有效避免多重条件判断,增强了封装性,简化了操作,降低出错概率;
    • 扩展性良好,策略类遵顼 里氏替换原则,可以很方便地进行策略扩展;

    缺点

    • 策略类数量增多,且所有策略类都必须对外暴露,以便客户端能进行选择;

    使用场景

    • 针对同一类型问题,有多种处理方式,每一种都能独立解决问题;
    • 算法需要自由切换的场景;
    • 需要屏蔽算法规则的场景;

    模式讲解

    首先来看下 策略模式 的通用 UML 类图:

     
    策略模式

    从 UML 类图中,我们可以看到,策略模式 主要包含三种角色:

    • 上下文角色(Context):用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略,算法的直接访问,封装可能存在的变化;
    • 抽象策略角色(Strategy):规定策略或算法的行为;
    • 具体策略角色(ConcreteStrategy):具体的策略或算法实现;

    以下是 策略模式 的通用代码:

    class Client {
        public static void main(String[] args) {
            //选择一个具体策略
            IStrategy strategy = new ConcreteStrategyA();
            //来一个上下文环境
            Context context = new Context(strategy);
            //客户端直接让上下文环境执行算法
            context.algorithm();
        }
    
        //抽象策略类 Strategy
        interface IStrategy {
            void algorithm();
        }
    
        //具体策略类 ConcreteStrategy
        static class ConcreteStrategyA implements IStrategy {
    
            @Override
            public void algorithm() {
                System.out.println("Strategy A");
            }
        }
    
        //具体策略类 ConcreteStrategy
        static class ConcreteStrategyB implements IStrategy {
    
            @Override
            public void algorithm() {
                System.out.println("Strategy B");
            }
        }
    
        //上下文环境
        static class Context {
            private IStrategy mStrategy;
    
            public Context(IStrategy strategy) {
                this.mStrategy = strategy;
            }
    
            public void algorithm() {
                this.mStrategy.algorithm();
            }
        }
    }

    举个例子

    例子:假设现在有两个数与一个运算符,要求使用该运算符操作这两个数。
    分析:直接思路:通过判断运算符符号,对这两个数进行运算。代码如下所示:

     static class Calculator {
            private static final String SYMBOL_ADD = "+";
            private static final String SYMBOL_SUB = "-";
    
            public int calc(int a, int b, final String symbol) {
                int result = 0;
                if (SYMBOL_ADD.equals(symbol)) {
                    result = a + b;
                } else if (SYMBOL_ADD.equals(symbol)) {
                    result = a - b;
                }
                return result;
            }
        }

    但是这样写的话,如果我们现在要扩展乘法*或除法/运算,那么就要在calc方法内增加对应的if...else判断,代码臃肿并且扩展性太低。
    而如果采用策略模式,将各种运算符的计算都归并到对应具体策略,这样,就能简化代码并且带来很好的扩展性,具体代码如下:

    class Client {
        public static void main(String[] args) {
            ICalculator calculator = new Add();
            Context context = new Context(calculator);
            int result = context.calc(1,2);
            System.out.println(result);
        }
    
        interface ICalculator {
            int calc(int a, int b);
        }
    
        static class Add implements ICalculator {
            @Override
            public int calc(int a, int b) {
                return a + b;
            }
        }
    
        static class Sub implements ICalculator {
            @Override
            public int calc(int a, int b) {
                return a - b;
            }
        }
    
        static class Multi implements ICalculator {
            @Override
            public int calc(int a, int b) {
                return a * b;
            }
        }
    
        static class Divide implements ICalculator {
            @Override
            public int calc(int a, int b) {
                return a / b;
            }
        }
    
        static class Context {
            private ICalculator mCalculator;
    
            public Context(ICalculator calculator) {
                this.mCalculator = calculator;
            }
    
            public int calc(int a, int b) {
                return this.mCalculator.calc(a, b);
            }
        }
    }

    从上面代码中,我们可以看到,我们完全消除了对运算符号进行判断的哪些if...else的冗余代码,取而代之的是客户端直接决定使用哪种算法,然后交由上下文获取结果。并且上面代码中我们还扩展了乘法Multi和除法Divide运算,所需要做的就只是扩展相应的策略类而已。

    注意策略模式 中的上下文环境(Context),其职责本来是隔离客户端与策略类的耦合,让客户端完全与上下文环境沟通,无需关系具体策略。但是从上面的代码中我们可以看到,客户端内部直接自己指定要哪种策略(ICalculator calculator = new Add()),客户端与具体策略类耦合了,而上下文环境在这里其的作用只是负责调度执行,获取结果,并没有完全起到隔离客户端与策略类的作用。一般可以通过简单工厂模式将具体策略的创建与客户端进行隔离,或者是通过 策略枚举 将上下文环境与具体策略类融合在一起,简化代码。当具体策略相对稳定时,推荐使用 策略枚举 简化代码,具体代码如下:

    class EnumClient {
        public static void main(String[] args) {
            int result = Calculator.ADD.calc(1, 2);
            System.out.println(result);
            // System.out.println(Calculator.ADD.getSymbol());
        }
    
        static enum Calculator {
            // 加法运算
            ADD("+") {
                @Override
                public int calc(int a, int b) {
                    return a + b;
                }
            },
            SUB("-") {
                @Override
                public int calc(int a, int b) {
                    return a - b;
                }
            };
            private String symbol;
    
            private Calculator(String symbol) {
                this.symbol = symbol;
            }
    
            public String getSymbol() {
                return this.symbol;
            }
    
            public abstract int calc(int a, int b);
        }
    }
  • 相关阅读:
    POJ1422 最小路径覆盖
    POJ1422 最小路径覆盖
    POJ1125 Floyd
    POJ1125 Floyd
    POJ2570 二进制,位运算,Floyd
    POJ2570 二进制,位运算,Floyd
    POJ2446 二分匹配
    POJ2536 二分图匹配
    POJ2536 二分图匹配
    POJ3692 最大点权独立集元素个数
  • 原文地址:https://www.cnblogs.com/pypua/p/11377433.html
Copyright © 2011-2022 走看看