设计模式之策略模式(C++实现)

更多设计模式参看: 设计模式之模式概述(模式汇总)(C++实现)

介绍

软件开发中,实现某一个功能有多条途径,每一条途径对应一种算法,通过策略模式的使用来实现灵活地选择解决途径,方便地增加新的解决途径。工厂模式是解耦对象的创建和使用,观察者模式是解耦观察者和被观察者。策略模式跟两者类似,也能起到解耦的作用,不过,它解耦的是策略的定义、创建、使用这三部分。

意图:

策略模式(Strategy Pattern): 定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化(客户端代指使用算法的代码)。

解决问题:

解决某一问题的一个算法族,允许用户从该算法族中任选一个算法来解决某一问题,同时可以方便地更换算法或者增加新的算法。

实现概述:

策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。策略的创建由工厂类来完成,封装策略创建的细节。策略模式包含一组策略可选,客户端代码如何选择使用哪个策略,有两种确定方法:编译时静态确定和运行时动态确定。其中,“运行时动态确定”才是策略模式最典型的应用场景。

应用场景:

  • 一个系统需要动态地在几种算法中选择一种,可以将这些算法封装到具体算法类中,它们都是一个抽象算法类的子类。
  • 转移一个对象中行为选择的多重条件选择语句到具体策略类里面,避免使用难以维护的多重条件选择语句。
  • 将算法以及复杂的、与算法相关的数据结构封装到具体策略类中,让客户端感知不到提高算法的保密性与安全性。

优点:

  • 符合开闭原则,策略模式易于扩展,可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。并为算法族提供一种管理机制和维护机制
  • 策略模式提供了一种算法的复用机制,客户端可以无差别地通过公共接口调用,利用里式替换原则,灵活使用不同的算法策略;避免了多重条件选择语句的使用

缺点:

  • 客户端需要知道所有的策略,并在适当的时候选择恰当的算法;
  • 会产生很多具体策略类,增加了系统中类的数量;
  • 客户端在同一时间只能使用一种策略,无法使用多个策略类。

模式结构

角色

  • Context(上下文类) :环境类是使用算法的角色,在解决某个问题时可以采用多种策略,通过对抽象策略类的引用实例定义所采用的策略。
  • Strategy(抽象策略类):声明所支持的算法的抽象方法,可以是抽象类或具体类,也可以是接口,是所有具体策略类的父类。
  • ConcreteStrategy(具体策略类):实现了在抽象策略类中声明的算法。

类图

在这里插入图片描述

代码示例

策略模式,通过选择不同的排序策略对数据排序

Context(上下文类):Context

Strategy(抽象策略类): Sort

ConcreteStrategy(具体策略类): BubbleSort、SelectionSort、InsertionSort

GitHub

StrategyPattern

Context(上下文类)

/// Context(上下文类):Context
class Context {
public:
    Context() {
        sortStrategy = nullptr;
        arr = nullptr;
        len = 0;
        std::cout << "Context Hello" << std::endl;
    }
    ~Context() {
        delete sortStrategy;
        std::cout << "Context Bye" << std::endl;
    }
    void setSortStrategy(Sort* sort){
        delete sortStrategy;
        sortStrategy = sort;
    }
    void sort(){
        sortStrategy->sort(arr, len);
        std::cout << "输出:" << std::endl;
        printSortedArr();
    }
    void setSortArr(int arr[], int len){
        this->arr = arr;
        this->len = len;
    }

    void printSortedArr(){
        std::string arrString;
        for (int i = 0; i < len; i++){
            arrString  += std::to_string(arr[i]) + " ";
        }
        std::cout << arrString <<std::endl;
    }

private:

    Sort* sortStrategy;
    int* arr;
    int len;
};

Strategy(抽象策略类)

/// Strategy(抽象策略类): Sort
class Sort {
public:
    virtual ~Sort() = default;
    virtual void sort(int arr[], int len) = 0;
protected:
    Sort() = default;
};

ConcreteStrategy(具体策略类)

/// ConcreteStrategy(具体策略类): BubbleSort、SelectionSort、InsertionSort
class BubbleSort : public Sort {
public:
    BubbleSort() {
        std::cout << "BubbleSort Hello" << std::endl;
    }
    ~BubbleSort() override {
        std::cout << "BubbleSort Bye" << std::endl;
    }
    /**冒泡排序
       1 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
       2 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
       3 针对所有的元素重复以上的步骤,除了最后一个;
       4 重复步骤1~3,直到排序完成。
     */
    /// 待排序区间 分  有序区间 无序区间, 每次循环从无序区间中两两比较并交换位置,将最大(最小)冒出来,放入有序区间
    void sort(int arr[], int len) override { // 冒泡排序
        for (int i = 0; i < len; ++i) {
            for (int j = 0; j < len - i - 1; ++j) {
                if (arr[j] > arr[j + 1]) { // 比较相邻的元素
                    auto temp = arr[j+1];        // 元素交换
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                }
            }

        }
    }
};


class SelectionSort : public Sort {
public:
    SelectionSort() {
        std::cout << "SelectionSort Hello" << std::endl;
    }
    ~SelectionSort() override {
        std::cout << "SelectionSort Bye" << std::endl;
    }
    /**选择排序
        1 初始状态:无序区为R[1..n],有序区为空;
        2 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。
        该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,
        使R[1..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
        3 n-1趟结束,数组有序化了。
     */
    /// 待排序区间 分  有序区间 无序区间, 每次循环从无序区间找出最小数据插入有序区间尾部
    void sort(int arr[], int len) override { // 选择排序 从小到大排序
        int minIndex;
        for (int i = 0; i < len - 1; ++i) {
            minIndex = i;
            for (int j = i + 1; j < len; ++j) { // 无序区
                if (arr[j] < arr[minIndex]) {
                    minIndex = j; // 找最小
                }
            }
            auto temp = arr[i]; // 交换
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    }
};


class InsertionSort : public Sort {
public:
    InsertionSort() {
        std::cout << "InsertionSort Hello" << std::endl;
    }
    ~InsertionSort() override {
        std::cout << "InsertionSort Bye" << std::endl;
    }
    /**插入排序
        1 从第一个元素开始,该元素可以认为已经被排序;
        2 取出下一个元素,在已经排序的元素序列中从后向前扫描;
        3 如果该元素(已排序)大于新元素,将该元素移到下一位置;
        4 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
        5 将新元素插入到该位置后;
        6 重复步骤2~5。
     */
    /// 待排序区间 分  有序区间 无序区间, 每次循环取无序区间第一个数据,在有序区间比较找到插入位置
    void sort(int arr[], int len) override { // 插入排序  从小到大排序
        int current;
        for (int i = 0; i < len - 1; i++) { // 从第一个元素开始
            current = arr[i + 1]; // 取出下一个元素 待排序
            int preIndex = i; // 已排序 最后一个元素
            while (preIndex >= 0 && arr[preIndex] > current ) { //  已排序大于待排序元素current,说明 current 在 arr[preIndex] 前面
                arr[preIndex + 1] = arr[preIndex]; // 移位 留出 插入位置
                preIndex--; // 从后向前扫描
            }
            arr[preIndex + 1] = current; // 插入
        }
    }
};

测试

int main() {
    auto* ctx = new Context();
    // 冒泡排序
    std::cout << "==========冒泡排序:==========" << std::endl;
    int arr1[] = { 3,40,-1,-45,44,56,9,-33 };
    ctx->setSortArr(arr1, sizeof(arr1)/sizeof(int));
    std::cout << "输入:" << std::endl;
    ctx->printSortedArr();
    ctx->setSortStrategy(new BubbleSort());
    ctx->sort();

    // 选择排序
    std::cout << "==========选择排序:==========" << std::endl;
    int arr2[] = { 13,4,-11,-5,14,6,9,-3 };
    ctx->setSortArr(arr2, sizeof(arr2)/sizeof(int));
    std::cout << "输入:" << std::endl;
    ctx->printSortedArr();
    ctx->setSortStrategy(new SelectionSort());
    ctx->sort();

    // 插入排序
    std::cout << "==========插入排序:==========" << std::endl;
    int arr3[] = { 30,-40,1,45,-4,1,-9,3 };
    ctx->setSortArr(arr3, sizeof(arr3)/sizeof(int));
    std::cout << "输入:" << std::endl;
    ctx->printSortedArr();
    ctx->setSortStrategy(new InsertionSort());
    ctx->sort();

    delete ctx;
    return 0;
}

输出

在这里插入图片描述

个人能力有限,如有错误之处或者其他建议,敬请告知欢迎探讨,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值