设计模式—策略(Strategy)模式

简介: 设计模式—策略(Strategy)模式

 一、概述

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

使用策略模式可以把行为和环境分割开来。环境类负责维持和查询行为类,各种算法则在具体策略类(ConcreteStrategy)中提供。由于算法和环境独立开来,算法的增减、修改都不会影响环境和客户端。当出现新的促销折扣或现有的折扣政策出现变化时,只需要实现新的策略类,并在客户端登记即可。策略模式相当于"可插入式(Pluggable)的算法"。

二、策略模式的结构

策略模式是对算法的包装,是把使用算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:"准备一组算法,并将每一个算法封装起来,使得它们可以互换。"

策略又称做政策(Policy)模式【GOF95】。下面是一个示意性的策略模式结构图:

image.gif

这个模式涉及到三个角色:

    • 环境(Context)角色:持有一个Strategy类的引用。
      • 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
        • 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

        三、 示意性源代码

        public abstract class Strategy {
            abstract public void AlgorithmInterface();
        }
        public class ConcreteStrategyA extends Strategy {
            // Methods
            @Override
            public void AlgorithmInterface() {
                System.out.print("Called ConcreteStrategyA.AlgorithmInterface()");
            }
        }
        // "ConcreteStrategyB"
        public class ConcreteStrategyB extends Strategy {
            // Methods
            public void AlgorithmInterface() {
                System.out.print("Called ConcreteStrategyB.AlgorithmInterface()");
            }
        }
        // "ConcreteStrategyC"
        public class ConcreteStrategyC extends Strategy {
            // Methods
            @Override
            public void AlgorithmInterface() {
                System.out.print("Called ConcreteStrategyC.AlgorithmInterface()");
            }
        }
        // "Context"
        public class Context {
            // Fields
            Strategy strategy;
            // Constructors
            public Context(Strategy strategy) {
                this.strategy = strategy;
            }
            // Methods
            public void ContextInterface() {
                strategy.AlgorithmInterface();
            }
        }
        /// <summary>
        /// Client test
        /// </summary>
        public class Client {
            public static void Main(String[] args) {
                // Three contexts following different strategies
                Context c = new Context(new ConcreteStrategyA());
                c.ContextInterface();
                Context d = new Context(new ConcreteStrategyB());
                d.ContextInterface();
                Context e = new Context(new ConcreteStrategyC());
                e.ContextInterface();
            }
        }

        image.gif

        四、 何时使用何种具体策略角色

        在学习策略模式时,学员常问的一个问题是:为什么不能从策略模式中看出哪一个具体策略适用于哪一种情况呢?

        答案非常简单,策略模式并不负责做这个决定。换言之,应当由客户端自己决定在什么情况下使用什么具体策略角色。策略模式仅仅封装算法,提供新算法插入到已有系统中,以及老算法从系统中"退休"的方便,策略模式并不决定在何时使用何种算法。

        五、 一个实际应用策略模式的例子

        下面的例子利用策略模式在排序对象中封装了不同的排序算法,这样以便允许客户端动态的替换排序策略(包括Quicksort、Shellsort和Mergesort)。

        abstract class SortStrategy {
            // Methods
            public abstract void Sort(List<String> list);
        }
        // "ConcreteStrategy"
        class QuickSort extends SortStrategy {
            // Methods
            @Override
            public void Sort(List<String> list) {
                // Default is Quicksort
                System.out.print("QuickSorted list ");
            }
        }
        // "ConcreteStrategy"
        class ShellSort extends SortStrategy {
            // Methods
            @Override
            public void Sort(List<String> list) {
                //list.ShellSort();
                System.out.print("ShellSorted list ");
            }
        }
        // "ConcreteStrategy"
        class MergeSort extends SortStrategy {
            // Methods
            @Override
            public void Sort(List<String> list) {
                //list.MergeSort();
                System.out.print("MergeSorted list ");
            }
        }
        // "Context"
        class SortedList {
            // Fields
            private List<String> list = new ArrayList<>();
            private SortStrategy sortstrategy;
            // Constructors
            public void SetSortStrategy(SortStrategy sortstrategy) {
                this.sortstrategy = sortstrategy;
            }
            // Methods
            public void Sort() {
                sortstrategy.Sort(list);
            }
            public void Add(String name) {
                list.add(name);
            }
            public void Display() {
                System.out.print(" name");
            }
        }
        /// <summary>
        /// StrategyApp test
        /// </summary>
        public class StrategyApp {
            public static void Main(String[] args) {
                // Two contexts following different strategies
                SortedList studentRecords = new SortedList();
                studentRecords.Add("Samual");
                studentRecords.Add("Jimmy");
                studentRecords.Add("Sandra");
                studentRecords.Add("Anna");
                studentRecords.Add("Vivek");
                studentRecords.SetSortStrategy(new QuickSort());
                studentRecords.Sort();
                studentRecords.Display();
            }
        }

        image.gif

        六、 在什么情况下应当使用策略模式

        在下面的情况下应当考虑使用策略模式:

        1. 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

        2. 一个系统需要动态地在几种算法中选择一种。那么这些算法可以包装到一个个的具体算法类里面,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,由于多态性原则,客户端可以选择使用任何一个具体算法类,并只持有一个数据类型是抽象算法类的对象。

        3. 一个系统的算法使用的数据不可以让客户端知道。策略模式可以避免让客户端涉及到不必要接触到的复杂的和只与算法有关的数据。

        4. 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句,并体现面向对象设计的概念。

        七、 策略模式的优点和缺点

        策略模式有很多优点和缺点。它的优点有:

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

        2. 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。

        3. 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。

        策略模式的缺点有:

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

        2. 策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。

        目录
        相关文章
        |
        6月前
        |
        设计模式 Java 数据库连接
        【设计模式】【创建型模式】工厂方法模式(Factory Methods)
        一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
        197 16
        |
        6月前
        |
        设计模式 负载均衡 监控
        并发设计模式实战系列(2):领导者/追随者模式
        🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
        202 0
        |
        6月前
        |
        设计模式 监控 Java
        并发设计模式实战系列(1):半同步/半异步模式
        🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第一章半同步/半异步(Half-Sync/Half-Async)模式,废话不多说直接开始~
        189 0
        |
        6月前
        |
        设计模式 安全 Java
        并发设计模式实战系列(12):不变模式(Immutable Object)
        🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第十二章,废话不多说直接开始~
        161 0
        |
        6月前
        |
        设计模式 算法 Java
        设计模式觉醒系列(04)策略模式|简单工厂模式的升级版
        本文介绍了简单工厂模式与策略模式的概念及其融合实践。简单工厂模式用于对象创建,通过隐藏实现细节简化代码;策略模式关注行为封装与切换,支持动态替换算法,增强灵活性。两者结合形成“策略工厂”,既简化对象创建又保持低耦合。文章通过支付案例演示了模式的应用,并强调实际开发中应根据需求选择合适的设计模式,避免生搬硬套。最后推荐了JVM调优、并发编程等技术专题,助力开发者提升技能。
        |
        6月前
        |
        设计模式 算法 搜索推荐
        【设计模式】【行为型模式】策略模式(Strategy)
        一、入门 什么是策略模式? 策略模式是一种行为设计模式,允许在运行时选择算法或行为。它将算法封装在独立的类中,使得它们可以互换,而不影响客户端代码。 为什么需要策略模式? 策略模式的主要目的是解决算法
        137 14
        |
        6月前
        |
        设计模式 Prometheus 监控
        并发设计模式实战系列(20):扇出/扇入模式(Fan-Out/Fan-In)(完结篇)
        🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第二十章,废话不多说直接开始~
        221 0
        |
        10月前
        |
        设计模式
        「全网最细 + 实战源码案例」设计模式——模式扩展(配置工厂)
        该设计通过配置文件和反射机制动态选择具体工厂,减少硬编码依赖,提升系统灵活性和扩展性。配置文件解耦、反射创建对象,新增产品族无需修改客户端代码。示例中,`CoffeeFactory`类加载配置文件并使用反射生成咖啡对象,客户端调用时只需指定名称即可获取对应产品实例。
        219 40
        |
        8月前
        |
        设计模式 Java 关系型数据库
        设计模式:工厂方法模式(Factory Method)
        工厂方法模式是一种创建型设计模式,通过将对象的创建延迟到子类实现解耦。其核心是抽象工厂声明工厂方法返回抽象产品,具体工厂重写该方法返回具体产品实例。适用于动态扩展产品类型、复杂创建逻辑和框架设计等场景,如日志记录器、数据库连接池等。优点包括符合开闭原则、解耦客户端与具体产品;缺点是可能增加类数量和复杂度。典型应用如Java集合框架、Spring BeanFactory等。
        |
        10月前
        |
        设计模式 Java
        「全网最细 + 实战源码案例」设计模式——生成器模式
        生成器模式(Builder Pattern)是一种创建型设计模式,用于分步骤构建复杂对象。它允许用户通过控制对象构造的过程,定制对象的组成部分,而无需直接实例化细节。该模式特别适合构建具有多种配置的复杂对象。其结构包括抽象建造者、具体建造者、指挥者和产品角色。适用于需要创建复杂对象且对象由多个部分组成、构造过程需对外隐藏或分离表示与构造的场景。优点在于更好的控制、代码复用和解耦性;缺点是增加复杂性和不适合简单对象。实现时需定义建造者接口、具体建造者类、指挥者类及产品类。链式调用是常见应用方式之一。
        162 12

        热门文章

        最新文章

        下一篇
        oss云网关配置