设计模式:策略模式

发布于:2025-09-15 ⋅ 阅读:(7) ⋅ 点赞:(0)

目录

一、引言

二、优化前的代码

三、策略模式

四、优化后的代码

五、策略模式总结

5.1 策略模式的优点

5.2 策略模式的缺点

5.3 策略模式的使用场景

六、结语



一、引言

某公司为某电影院开发了一套影院售票系统,在该系统中需要为不同类型的用户提供不同的电影票打折方式,具体打折方案如下:

1)学生票凭学生证可享受票价的8折优惠。

2)儿童可享受票价的6折优惠。

3)VIP用户可享受票价的5折优惠。

4)其他用户按票价正常收费。                                                                                                          

本文的重点不在于代码的实现,而在于代码的设计,所以上述例子就尽可能的简单,方便代码的编写。接下来我们将上面的例子转化为代码。

二、优化前的代码

class MovieTicket {
public:
    void studentDiscount() { _price *= 0.8; } //学生
    void childDiscount() { _price *= 0.6; }   //儿童
    void normalPrice() { _price *= 1; }       //正常
    void vipDiscount() { _price *= 0.5; }     //VIP
    double getPrice() { return _price; }
private:
    double _price;
};

核心代码不到10行,便可以将影院售票系统的功能全部实现了,客户端根据用户类型自行选择打折方式(也就是要调用哪个函数)即可。这个代码有问题吗?没有,但是不优雅。

1)如果需要新增打折方式或者修改现有的打折方式的话,需要直接修改现有类的代码。这就违反了面向对象设计的五大原则之一的开闭原则——对扩展开放,对修改关闭。

2)耦合度太高。算法直接嵌入到使用它的类中,导致业务逻辑与具体实现紧密耦合。我们追求的是高内聚,低耦合。

3)复用性差。相同的算法无法在不同上下文中复用,必须重新实现或复制代码。例如,支付处理中不同支付方式(信用卡、支付宝)的实现,若硬编码在订单类中会导致代码重复。

4)在一些情况下,我们并不希望对外暴露底层的算法实现。业务类你只需要通过调用提供的方法来处理好业务逻辑即可,不需要知道底层的算法实现。

上述这些问题,策略模式都能优雅的解决。

三、策略模式

策略模式的定义如下:

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

Strategy Pattern:Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

这个定义挺抽象的,我们看看它的模式结构图。

策略模式包含3个角色。

1)Context(上下文类):用来处理业务逻辑的类。

2)Strategy(抽象策略类):为所要提供的算法声明抽象方法。

3)ConcreteStrategy(具体策略类):继承抽象策略类,然后重写抽象策略类中的方法。

综上,可以看到,策略模式就是要对算法进行封装,业务类只需要调用相应的算法就好了,算法的具体实现则交给抽象策略类的子类来完成。

典型代码:

首先定义一个抽象策略类,其中包含一个纯虚函数。

class AbstractStrategy {
public:
    virtual void algorithm() = 0;//声明抽象方法
};

其次,定义具体策略类,继承自抽象策略类,然后每个具体策略类重写虚方法。

class ConcreteStrategy1 : public AbstractStrategy {
public:
    void algorithm() override {
        //算法1...
    }
};

class ConcreteStrategy2 : public AbstractStrategy {
public:
    void algorithm() override {
        //算法2...
    }
};

然后,定义一个上下文类来处理业务逻辑。

class Context {
public:
    void setStrategy(AbstractStrategy* strategy) {
        _strategy = strategy;
    }
    void doSomething() {
        _strategy->algorithm();
    }
private:
    AbstractStrategy* _strategy;
};

最后,编写客户端的调用逻辑。

Context context;
AbstractStrategy* strategy = new ConcreteStrategy1();
context.setStrategy(strategy);
context.doSomething();

基于策略模式实现的代码就这样串联起来了。

四、优化后的代码

了解了策略模式后,我们来对前面影院售票系统的代码做优化。

class AbstractDiscount {
public:
    virtual double calculate(double price) = 0; //声明抽象方法
};

class StudentDiscount : public AbstractDiscount {
public:
    double calculate(double price) override {
        //学生折扣
        return price * 0.8;
    }
};

class ChildDiscount : public AbstractDiscount {
public:
    double calculate(double price) override {
        //儿童折扣
        return price * 0.6;
    }
};

class NormalPrice : public AbstractDiscount {
public:
    double calculate(double price) override {
        //正常价格
        return price;
    }
};

class VipDiscount : public AbstractDiscount {
public:
    double calculate(double price) override {
        //VIP折扣
        return price * 0.5;
    }
};

class MovieTicket {
public:
    void setDiscount(AbstractDiscount* discount) {
        _discount = discount;
    }
    double getPrice(double price) {
        return _discount->calculate(price);
    }
private:
    AbstractDiscount* _discount;
};

int main()
{
    MovieTicket ticket;
    AbstractDiscount* discount = new StudentDiscount();
    ticket.setDiscount(discount);
    double price = ticket.getPrice(100); //80
    std::cout << "final price: " << price << std::endl;

    return 0;
}

如果需要增加新的打折方式,原有代码无需改动,只需要增加一个新的折扣类作为抽象折扣类的子类,实现对应的打折方法即可。

五、策略模式总结

5.1 策略模式的优点

1)策略模式支持用户在不修改原有代码的基础上新增算法。

2)策略模式可以降低代码的耦合度,业务处理无需关心具体的算法实现。

5.2 策略模式的缺点

1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

2)策略模式将造成系统产生很多的具体策略类,每个细小的改动都要增加一个新的具体策略类。

5.3 策略模式的使用场景

1)一个系统需要动态的在多种算法中选择一种,那么可以将这些算法封装到一个个的具体策略类中,需要哪一个就调用哪一个。

2)不希望客户端知道具体的算法实现,只管调用。

六、结语

在写这篇文章前,看了一些大佬的文章,里面涉及到一些复杂的应用场景。但小编学识有限,还没有接触到这些复杂场景,很难说清楚,所以举的例子就尽可能的简单。同时,设计模式有很多种,它们不是孤立的,可以结合使用。


完~