策略模式

策略 Strategy

策略模式是一种行为设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换。


为什么要使用?

策略模式的对象职责:

定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换。

👉 为了提高代码的可维护性。

👉 为了动态快速地替换更多的算法。

👉 为了应对需要频繁更换策略的场景。

💡 策略模式对算法起到了很好的封装作用,对于一些老旧的算法可以很方便地进行替换和升级。


模式结构

  • 上下文(Context)维护指向具体策略的引用,且仅通过策略接口与该对象进行交流。当上下文需要运行算法时,它会在其已连接的策略对象上调用执行方法。上下文不清楚其所涉及的策略类型与算法的执行方式。
  • 策略(Strategy)接口是所有具体策略的通用接口,它声明了一个上下文用于执行策略的方法。
  • 具体策略(Concrete Strategies)实现了上下文所用算法的各种不同变体。
  • 客户端(Client)会创建一个特定策略对象并将其传递给上下文。上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。

策略模式的类图:

在这里插入图片描述


模式实现

该示例使用策略模式将不同的排序算法(冒泡排序和插入排序)变为一个个单独的类。通过在 IntTypeSort 类中注入不同的策略,来调用不同的排序算法。

示例程序的类图

在这里插入图片描述

代码实现
上下文
package example;

/** int类型排序的上下文 */
public class IntTypeSort {
    /** 排序策略 */
    private ISort sortStrategy;

    public IntTypeSort(ISort sortStrategy) {
        this.sortStrategy = sortStrategy;
    }

    /**
     * 从小到大顺序排序
     * @param arrays 待排序数组
     * @return 排序好的数组
     */
    public int[] getOrder(int[] arrays) {
        return sortStrategy.sort(arrays, true);
    }

    /**
     * 从大到小逆序排序
     * @param arrays 待排序数组
     * @return 排序好的数组
     */
    public int[] getReverseOrder(int[] arrays) {
        return sortStrategy.sort(arrays, false);
    }
}
策略接口
package example;

/** 排序策略接口 */
public interface ISort {
    /**
     * 排序算法
     * @param arrays 待排序数组
     * @param flag true从小到大顺序排序,false从大到小逆序排序
     * @return 排序好的数组
     */
    int[] sort(int[] arrays, boolean flag);
}
具体策略
package example;

/** 冒泡排序策略 */
public class BubbleSort implements ISort {
    /**
     * 排序算法
     * @param arrays 待排序数组
     * @param flag   true从小到大顺序排序,false从大到小逆序排序
     * @return 排序好的数组
     */
    @Override
    public int[] sort(int[] arrays, boolean flag) {
        if (arrays.length == 0) {
            return arrays;
        }
        for (int i = 0; i < arrays.length; i++)
        {
            for (int j = 0; j < arrays.length - 1 - i; j++)
            {
                if (arrays[j + 1] < arrays[j]) {
                    int temp = arrays[j + 1];
                    arrays[j + 1] = arrays[j];
                    arrays[j] = temp;
                }
            }
        }
        // 从大到小排序
        if (!flag) {
            int len = arrays.length;
            int[] temp = new int[len];
            for (int i = 0; i < len; i++) {
                temp[i] = arrays[len - i - 1];
            }
            return temp;
        }
        return arrays;
    }
}
package example;

/** 插入排序策略 */
public class InsertionSort implements ISort {
    /**
     * 排序算法
     * @param arrays 待排序数组
     * @param flag   true从小到大顺序排序,false从大到小逆序排序
     * @return 排序好的数组
     */
    @Override
    public int[] sort(int[] arrays, boolean flag) {
        if (arrays.length == 0)
        {
            return arrays;
        }
        int current;
        for (int i = 0; i < arrays.length - 1; i++) {
            current = arrays[i + 1];
            int preIndex = i;
            while (preIndex >= 0 && current < arrays[preIndex]) {
                arrays[preIndex + 1] = arrays[preIndex];
                preIndex--;
            }
            arrays[preIndex + 1] = current;
        }
        // 从大到小排序
        if (!flag) {
            int len = arrays.length;
            int[] temp = new int[len];
            for (int i = 0; i < len; i++) {
                temp[i] = arrays[len - i - 1];
            }
            return temp;
        }
        return arrays;
    }
}
代码测试
import example.IntTypeSort;
import example.BubbleSort;
import example.InsertionSort;

import java.util.Arrays;

/** 测试策略模式 */
public class Test {
    public static void main(String[] args) {
        int[] arrays = new int[]{6, 1, 4, 9, 2, 3, 7, 5, 8};
        System.out.print("原始数组为:");
        Arrays.stream(arrays).forEach(item -> System.out.print(item + " "));
        System.out.println("\n");

        IntTypeSort bubbleSort = new IntTypeSort(new BubbleSort());
        IntTypeSort insertionSort = new IntTypeSort(new InsertionSort());

        System.out.println("冒泡排序策略:");
        System.out.print("从小到大顺序排序:");
        Arrays.stream(bubbleSort.getOrder(arrays)).forEach(item -> System.out.print(item + " "));
        System.out.println();
        System.out.print("从大到小逆序排序:");
        Arrays.stream(bubbleSort.getReverseOrder(arrays)).forEach(item -> System.out.print(item + " "));
        System.out.println();

        System.out.println("\n----------------------- 分割线 -----------------------\n");

        System.out.println("插入排序策略:");
        System.out.print("从小到大顺序排序:");
        Arrays.stream(insertionSort.getOrder(arrays)).forEach(item -> System.out.print(item + " "));
        System.out.println();
        System.out.print("从大到小逆序排序:");
        Arrays.stream(insertionSort.getReverseOrder(arrays)).forEach(item -> System.out.print(item + " "));
        System.out.println();
    }
}
输出结果
原始数组为:6 1 4 9 2 3 7 5 8 

冒泡排序策略:
从小到大顺序排序:1 2 3 4 5 6 7 8 9 
从大到小逆序排序:9 8 7 6 5 4 3 2 1 

----------------------- 分割线 -----------------------

插入排序策略:
从小到大顺序排序:1 2 3 4 5 6 7 8 9 
从大到小逆序排序:9 8 7 6 5 4 3 2 1 

使用 IntTypeSort bubbleSort = new IntTypeSort(new BubbleSort());IntTypeSort insertionSort = new IntTypeSort(new InsertionSort()); 注入不同策略,再根据不同策略执行排序。


常用场景和解决方案

  • 系统中需要动态切换几种算法的场景。
  • 使用多重的条件选择语句来实现的业务场景。例如,当类中使用了复杂条件运算符以在同一算法的不同变体中切换时。
  • 只希望客户端选择已经封装好的算法场景而不关心算法实现细节。
  • 如果算法在上下文的逻辑不是非常重要,策略模式可以让你分离使用策略和创建策略的场景。
  • 当你有许多仅在执行某些行为时略有不同的相似类时,可使用策略模式。策略模式让你能将不同行为抽取到一个独立类层次结构中,并将原始类组合成同一个,从而减少重复代码。

模式的优缺点

优点缺点
你可以在运行时切换对象内的算法,提升代码灵活性。如果你的算法极少发生改变,那么没有任何理由引入新的类和接口。使用该模式只会让程序过于复杂。
你可以将算法的实现和使用算法的代码隔离开来。客户端必须知晓策略间的不同——它需要选择合适的策略。客户端的学习成本变高。
提供了一种管理多个不同算法策略的方法,能够降低使用多重 if-else 嵌套语句的理解难度。许多现代编程语言支持函数类型功能,允许你在一组匿名函数中实现不同版本的算法。这样,你使用这些函数的方式就和使用策略对象时完全相同,无需借助额外的类和接口来保持代码简洁。
开闭原则。你无需对上下文进行修改就能够引入新的策略,提供良好的代码扩展性。具体策略类的数量会剧增,增加维护成本。
你可以使用组合来代替继承。

拓展知识

  • 装饰模式可让你更改对象的外表,策略则让你能够改变其本质。
  • 桥接模式、状态模式和策略模式(在某种程度上包括适配器模式)模式的接口非常相似。实际上,它们都基于组合模式——即将工作委派给其他对象,不过也各自解决了不同的问题。模式并不只是以特定方式组织代码的配方,你还可以使用它们来和其他开发者讨论模式所解决的问题。
  • 模板方法模式基于继承机制:它允许你通过扩展子类中的部分内容来改变部分算法。策略基于组合机制:你可以通过对相应行为提供不同的策略来改变对象的部分行为。模板方法在类层次上运作,因此它是静态的。策略在对象层次上运作,因此允许在运行时切换行为。


🔙 设计模式

📌最后:希望本文能够给您提供帮助,文章中有不懂或不正确的地方,请在下方评论区💬留言!

🔗参考文献:

🌐 设计模式 --refactoringguru

▶️ bilibili-趣学设计模式;黄靖锋. --拉勾教育

📖 图解设计模式 /(日)结城浩著;杨文轩译. --北京:人民邮电出版社,2017.1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值