用最简单的方式上手设计模式--策略模式(一直更新!!)

面试的时候,设计模式好像问的挺多,但是cpper大部分都不会去买书或者看视频,而且有些视频讲的不明不白的,于是我决定了!!!开一个专栏专门唠设计模式,不定期更新。

我觉得上手最快的方式,就是先了解一个大概的设计理念,然后熟悉设计模式的模板,对,设计模式是实践出来的,所以有模板,一定要背下来,然后再了解一下运用场景,解决了什么问题,把它运用起来,就OK啦!

这个系列将不定期更新,接下来先讲解策略模式:

什么是策略模式

策略模式是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。通过使用策略模式,可以在运行时选择算法的行为。

下面是一个使用C++实现的策略模式的示例:

#include <iostream>

// 定义策略接口
class Strategy {
public:
    virtual void algorithm() = 0;
};

// 策略的具体实现A
class ConcreteStrategyA : public Strategy {
public:
    void algorithm() override {
        std::cout << "Using ConcreteStrategyA" << std::endl;
    }
};

// 策略的具体实现B
class ConcreteStrategyB : public Strategy {
public:
    void algorithm() override {
        std::cout << "Using ConcreteStrategyB" << std::endl;
    }
};

// 定义策略上下文,负责维护策略对象并提供调用接口
class Context {
public:
    Context(Strategy* strategy) : strategy_(strategy) {}

    void set_strategy(Strategy* strategy) {
        strategy_ = strategy;
    }

    void execute_algorithm() {
        strategy_->algorithm();
    }

private:
    Strategy* strategy_;
};

int main() {
    // 创建策略上下文,使用策略A
    ConcreteStrategyA strategy_a;
    Context context(&strategy_a);

    // 调用策略A的算法
    context.execute_algorithm();

    // 切换策略为B
    ConcreteStrategyB strategy_b;
    context.set_strategy(&strategy_b);

    // 调用策略B的算法
    context.execute_algorithm();

    return 0;
}

上面Strategy 是策略接口,ConcreteStrategyAConcreteStrategyB 是具体的策略实现,Context 是策略上下文,负责维护策略对象并提供调用接口。客户端可以通过调用 Contextexecute_algorithm 方法来执行具体的策略算法。

在运行时,客户端可以通过修改策略上下文中的策略对象,来选择使用不同的策略算法。

在上面的代码中,策略对象通过指针传递,而不是值传递,这可以减少对象复制的开销。同时,策略对象的生命周期也需要客户端自行管理。

应用场景

策略模式适用于以下场景:

  1. 多算法替换:当一个系统需要根据不同的情况选择不同的算法或策略时,可以使用策略模式。例如,对于排序算法,可以根据数据量大小选择使用快速排序、归并排序或插入排序等不同的排序策略。

  2. 避免条件语句:当代码中存在大量的条件语句,根据不同条件执行不同的操作时,可以考虑使用策略模式来消除这些条件语句。通过将每个条件分支封装成一个独立的策略类,可以简化代码逻辑并提高代码的可维护性和可扩展性。

  3. 算法的扩展和维护:当系统中的算法经常变化或需要新增算法时,使用策略模式可以方便地扩展和维护代码。只需要添加新的策略类,并将其注册到策略上下文中,而无需修改现有的代码。

  4. 隔离变化:策略模式可以将不同的行为封装到不同的策略类中,从而将变化的部分和稳定的部分分离开来。这样,在需求变更时,只需要修改相应的策略类或添加新的策略类,而不会对其他部分造成影响。

  5. 可扩展的插件系统:通过策略模式,可以实现一个可扩展的插件系统。每个插件对应一个策略类,系统可以根据需要动态加载和使用不同的插件。

总结一下,策略模式适用于需要在运行时选择不同算法或策略的场景,以及需要将算法的实现与调用代码解耦的情况。它能够提高代码的灵活性、可维护性和可扩展性。常见的应用包括排序算法、支付方式选择、日志记录级别等。

一个详细具体的场景

假设我们正在开发一个电商平台,其中有一个商品促销活动模块。在该模块中,不同的促销活动可以根据不同的策略来计算商品的折扣价格。这里可以使用策略模式来实现。

具体示例代码如下:

#include <iostream>
#include <string>

// 抽象策略类
class DiscountStrategy {
public:
    virtual double applyDiscount(double price) const = 0;
};

// 具体策略类1:固定折扣
class FixedDiscountStrategy : public DiscountStrategy {
public:
    double applyDiscount(double price) const override {
        return price * 0.8;  // 固定折扣为80%
    }
};

// 具体策略类2:满减
class ThresholdDiscountStrategy : public DiscountStrategy {
public:
    double applyDiscount(double price) const override {
        if (price >= 100) {
            return price - 20;  // 满100减20
        } else {
            return price;
        }
    }
};

// 上下文类
class PromotionContext {
private:
    DiscountStrategy* strategy;

public:
    PromotionContext(DiscountStrategy* strategy) : strategy(strategy) {}

    double calculateDiscountedPrice(double price) const {
        return strategy->applyDiscount(price);
    }
};

int main() {
    // 创建具体策略对象
    DiscountStrategy* strategy1 = new FixedDiscountStrategy();
    DiscountStrategy* strategy2 = new ThresholdDiscountStrategy();

    // 创建上下文对象,并指定具体策略
    PromotionContext context1(strategy1);
    PromotionContext context2(strategy2);

    double price = 120;  // 商品原价

    // 计算折扣后的价格
    double discountedPrice1 = context1.calculateDiscountedPrice(price);
    double discountedPrice2 = context2.calculateDiscountedPrice(price);

    std::cout << "原价: " << price << std::endl;
    std::cout << "固定折扣后的价格: " << discountedPrice1 << std::endl;
    std::cout << "满减后的价格: " << discountedPrice2 << std::endl;

    // 释放资源
    delete strategy1;
    delete strategy2;

    return 0;
}

在上述示例中,我们定义了抽象策略类 DiscountStrategy,它声明了一个纯虚函数 applyDiscount() 用于计算折扣价格。然后,我们实现了两个具体策略类 FixedDiscountStrategyThresholdDiscountStrategy,它们分别根据不同的折扣策略来计算商品的折扣价格。

我们还定义了上下文类 PromotionContext,它包含一个指向抽象策略类的指针,并通过构造函数接收具体策略对象。上下文类提供了一个方法 calculateDiscountedPrice(),用于调用具体策略对象的 applyDiscount() 方法计算折扣后的价格。

在主函数中,我们创建了具体策略对象,并将其传递给上下文对象,然后

调用上下文对象的方法来计算折扣后的价格。这样,我们可以根据不同的策略计算商品的折扣价格,而不需要修改客户端代码。

这个例子中的电商平台可以根据需要定义更多的促销策略,例如打折、满减、满赠等,通过策略模式可以很方便地扩展和切换不同的促销策略,而不需要修改已有的代码逻辑。

代码示例

以下是十个策略模式的C++代码示例:

  1. 策略模式的基本结构
class Strategy {
public:
    virtual void execute() = 0;
};

class ConcreteStrategyA : public Strategy {
public:
    void execute() override {
        // 实现具体策略A的代码
    }
};

class ConcreteStrategyB : public Strategy {
public:
    void execute() override {
        // 实现具体策略B的代码
    }
};

class Context {
private:
    Strategy* strategy_;
public:
    Context(Strategy* strategy) : strategy_(strategy) {}
    void executeStrategy() {
        strategy_->execute();
    }
};
  1. 策略模式的使用示例
int main() {
    Strategy* strategyA = new ConcreteStrategyA();
    Strategy* strategyB = new ConcreteStrategyB();

    Context contextA(strategyA);
    Context contextB(strategyB);

    contextA.executeStrategy();
    contextB.executeStrategy();

    delete strategyA;
    delete strategyB;

    return 0;
}
  1. 策略模式的简化版
class Context {
private:
    function<void()> strategy_;
public:
    Context(function<void()> strategy) : strategy_(strategy) {}
    void executeStrategy() {
        strategy_();
    }
};

int main() {
    Context contextA([]() {
        // 实现具体策略A的代码
    });

    Context contextB([]() {
        // 实现具体策略B的代码
    });

    contextA.executeStrategy();
    contextB.executeStrategy();

    return 0;
}
  1. 策略模式的模板版
template<typename T>
class Strategy {
public:
    virtual void execute(T& data) = 0;
};

template<typename T>
class ConcreteStrategyA : public Strategy<T> {
public:
    void execute(T& data) override {
        // 实现具体策略A的代码
    }
};

template<typename T>
class ConcreteStrategyB : public Strategy<T> {
public:
    void execute(T& data) override {
        // 实现具体策略B的代码
    }
};

template<typename T>
class Context {
private:
    Strategy<T>* strategy_;
public:
    Context(Strategy<T>* strategy) : strategy_(strategy) {}
    void executeStrategy(T& data) {
        strategy_->execute(data);
    }
};

int main() {
    Context<int> contextA(new ConcreteStrategyA<int>());
    Context<int> contextB(new ConcreteStrategyB<int>());

    int data = 10;

    contextA.executeStrategy(data);
    contextB.executeStrategy(data);

    return 0;
}
  1. 策略模式的模板版简化版
template<typename T>
class Context {
private:
    function<void(T&)> strategy_;
public:
    Context(function<void(T&)> strategy) : strategy_(strategy) {}
    void executeStrategy(T& data) {
        strategy_(data);
    }
};

int main() {
    Context<int> contextA([](int& data) {
        // 实现具体策略A的代码
    });

    Context<int> contextB([](int& data) {
        // 实现具体策略B的代码
    });

    int data = 10;

    contextA.executeStrategy(data);
    contextB.executeStrategy(data);

    return 0;
}
  1. 策略模式的多态版
class Strategy {
public:
    virtual void execute() = 0;
};

class ConcreteStrategyA : public Strategy {
public:
    void execute() override {
        // 实现具体策略A的代码
    }
};

class ConcreteStrategyB : public Strategy {
public:
    void execute() override {
        // 实现具体策略B的代码
    }
};

class Context {
private:
    Strategy* strategy_;
public:
    Context(Strategy* strategy) : strategy_(strategy) {}
    void executeStrategy() {
        strategy_->execute();
    }
};

int main() {
    vector<Strategy*> strategies = {new ConcreteStrategyA(), new ConcreteStrategyB()};

    for (auto strategy : strategies) {
        Context context(strategy);
        context.executeStrategy();
    }

    for (auto strategy : strategies) {
        delete strategy;
    }

    return 0;
}
  1. 策略模式的多态版简化版
class Context {
private:
    function<void()> strategy_;
public:
    Context(function<void()> strategy) : strategy_(strategy) {}
    void executeStrategy() {
        strategy_();
    }
};

int main() {
    vector<function<void()>> strategies = {
        []() {
            // 实现具体策略A的代码
        },
        []() {
            // 实现具体策略B的代码
        }
    };

    for (auto strategy : strategies) {
        Context context(strategy);
        context.executeStrategy();
    }

    return 0;
}
  1. 策略模式的函数指针版
typedef void (*Strategy)(void);

void concreteStrategyA() {
    // 实现具体策略A的代码
}

void concreteStrategyB() {
    // 实现具体策略B的代码
}

class Context {
private:
    Strategy strategy_;
public:
    Context(Strategy strategy) : strategy_(strategy) {}
    void executeStrategy() {
        strategy_();
    }
};

int main() {
    Context contextA(concreteStrategyA);
    Context contextB(concreteStrategyB);

    contextA.executeStrategy();
    contextB.executeStrategy();

    return 0;
}
  1. 策略模式的函数对象版
class ConcreteStrategyA {
public:
    void operator()() {
        // 实现具体策略A的代码
    }
};

class ConcreteStrategyB {
public:
    void operator()() {
        // 实现具体策略B的代码
    }
};

class Context {
private:
    function<void()> strategy_;
public:
    Context(function<void()> strategy) : strategy_(strategy) {}
    void executeStrategy() {
        strategy_();
    }
};

int main() {
    Context contextA(ConcreteStrategyA());
    Context contextB(ConcreteStrategyB());

    contextA.executeStrategy();
    contextB.executeStrategy();

    return 0;
}
  1. 策略模式的Lambda表达式版
class Context {
private:
    function<void()> strategy_;
public:
    Context(function<void()> strategy) : strategy_(strategy) {}
    void executeStrategy() {
        strategy_();
    }
};

int main() {
    Context contextA([]() {
        // 实现具体策略A的代码
    });

    Context contextB([]() {
        // 实现具体策略B的代码
    });

    contextA.executeStrategy();
    contextB.executeStrategy();

    return 0;
}

以上是策略模式不同的实现,但是结构又是相同的,设计模式是结构化的东西,应该知道在什么时候适合用什么设计模式,能够做到融会贯通,将这种设计模式应用起来,明白应用场景,才能说是学明白了!

分享到此结束,喜欢的同学点赞收藏,祝大家秋招斩获满意的offer知乎连接!

#设计模式#
全部评论
说的很好,期待下期
点赞 回复 分享
发布于 2023-06-06 13:49 江苏

相关推荐

像好涩一样好学:这公司我也拿过 基本明确周六加班 工资还凑活 另外下次镜头往上点儿
点赞 评论 收藏
分享
3 3 评论
分享
牛客网
牛客企业服务