在软件设计中,状态模式(State Pattern)和策略模式(Strategy Pattern)都是常用的设计模式,尽管它们在实现上有一些相似之处,但它们解决的问题和应用场景有所不同。
1. 状态模式概述
1.1 定义
状态模式是一种行为设计模式,它允许对象在内部状态改变时改变其行为。状态模式将与某个状态相关的行为局部化到一个类中,从而将不同状态的行为分离开来,使得对象在不同的状态下具有不同的行为。
1.2 结构
- Context(上下文):维护一个State实例,这个实例定义了当前的状态。
- State(状态):定义一个接口,以封装与Context的一个特定状态相关的行为。
- ConcreteState(具体状态):实现State接口,并且封装了与Context的一个具体状态相关的行为。
1.3 状态模式示例
以一个简单的糖果机为例,糖果机有三种状态:没有硬币(NoCoinState)、有硬币(HasCoinState)和售出糖果(SoldState)。
#include <iostream>
#include <memory>
class CandyMachine;
class State {
public:
virtual ~State() = default;
virtual void insertCoin(CandyMachine& machine) = 0;
virtual void ejectCoin(CandyMachine& machine) = 0;
virtual void turnCrank(CandyMachine& machine) = 0;
virtual void dispense(CandyMachine& machine) = 0;
};
class NoCoinState : public State {
public:
void insertCoin(CandyMachine& machine) override;
void ejectCoin(CandyMachine& machine) override;
void turnCrank(CandyMachine& machine) override;
void dispense(CandyMachine& machine) override {
std::cout << "You need to insert a coin first.\n";
}
};
class HasCoinState : public State {
public:
void insertCoin(CandyMachine& machine) override {
std::cout << "You can't insert another coin.\n";
}
void ejectCoin(CandyMachine& machine) override;
void turnCrank(CandyMachine& machine) override;
void dispense(CandyMachine& machine) override {
std::cout << "Turn the crank to get candy.\n";
}
};
class SoldState : public State {
public:
void insertCoin(CandyMachine& machine) override {
std::cout << "Please wait, we're already giving you a candy.\n";
}
void ejectCoin(CandyMachine& machine) override {
std::cout << "Sorry, you already turned the crank.\n";
}
void turnCrank(CandyMachine& machine) override {
std::cout << "Turning twice doesn't get you another candy!\n";
}
void dispense(CandyMachine& machine) override;
};
class CandyMachine {
public:
CandyMachine() : state(std::make_shared<NoCoinState>()) {}
void setState(std::shared_ptr<State> newState) {
state = newState;
}
void insertCoin() {
state->insertCoin(*this);
}
void ejectCoin() {
state->ejectCoin(*this);
}
void turnCrank() {
state->turnCrank(*this);
state->dispense(*this);
}
private:
std::shared_ptr<State> state;
friend class NoCoinState;
friend class HasCoinState;
friend class SoldState;
std::shared_ptr<State> noCoinState = std::make_shared<NoCoinState>();
std::shared_ptr<State> hasCoinState = std::make_shared<HasCoinState>();
std::shared_ptr<State> soldState = std::make_shared<SoldState>();
};
void NoCoinState::insertCoin(CandyMachine& machine) {
std::cout << "You inserted a coin.\n";
machine.setState(machine.hasCoinState);
}
void NoCoinState::ejectCoin(CandyMachine& machine) {
std::cout << "You haven't inserted a coin.\n";
}
void NoCoinState::turnCrank(CandyMachine& machine) {
std::cout << "You turned, but there's no coin.\n";
}
void HasCoinState::ejectCoin(CandyMachine& machine) {
std::cout << "Coin returned.\n";
machine.setState(machine.noCoinState);
}
void HasCoinState::turnCrank(CandyMachine& machine) {
std::cout << "You turned...\n";
machine.setState(machine.soldState);
}
void SoldState::dispense(CandyMachine& machine) {
std::cout << "A candy comes rolling out the slot.\n";
machine.setState(machine.noCoinState);
}
2. 策略模式概述
2.1 定义
策略模式是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法独立于使用它的客户(Context)而独立变化。
2.2 结构
- Context(上下文):持有一个Strategy的引用。
- Strategy(策略):定义算法的接口。
- ConcreteStrategy(具体策略):实现Strategy接口,提供具体的算法实现。
2.3 策略模式示例
以一个简单的计算器为例,可以执行加法和减法操作。
#include <iostream>
#include <memory>
class Strategy {
public:
virtual ~Strategy() = default;
virtual int execute(int a, int b) = 0;
};
class AddStrategy : public Strategy {
public:
int execute(int a, int b) override {
return a + b;
}
};
class SubtractStrategy : public Strategy {
public:
int execute(int a, int b) override {
return a - b;
}
};
class Calculator {
public:
void setStrategy(std::shared_ptr<Strategy> newStrategy) {
strategy = newStrategy;
}
int calculate(int a, int b) {
return strategy->execute(a, b);
}
private:
std::shared_ptr<Strategy> strategy;
};
int main() {
Calculator calculator;
calculator.setStrategy(std::make_shared<AddStrategy>());
std::cout << "10 + 5 = " << calculator.calculate(10, 5) << std::endl;
calculator.setStrategy(std::make_shared<SubtractStrategy>());
std::cout << "10 - 5 = " << calculator.calculate(10, 5) << std::endl;
return 0;
}
3. 状态模式与策略模式的异同
3.1 相似点
- 抽象类和接口:两种模式都使用抽象类或接口来定义行为,具体行为由子类实现。
- 多态性:两种模式都利用多态性来选择不同的行为或算法,避免了条件语句的使用。
- 可扩展性:都可以通过增加新的子类来扩展行为或算法,而无需修改现有代码,符合开闭原则(OCP)。
3.2 不同点
-
意图不同:
- 状态模式:关注的是对象的状态转换。对象在不同状态下表现出不同的行为。状态之间通常有固定的转换逻辑。
- 策略模式:关注的是算法的替换。它使得算法可以互相替换,而不依赖具体的算法实现。
-
上下文的关系:
- 状态模式:上下文(Context)和状态(State)之间通常是紧耦合的,状态知道并能改变上下文的状态。
- 策略模式:上下文(Context)和策略(Strategy)之间是松耦合的,策略只是被上下文使用,策略本身不关心上下文的状态。
-
应用场景:
- 状态模式:适用于对象需要在不同状态下表现出不同的行为,并且这些状态之间有复杂的转换逻辑。
- 策略模式:适用于需要在运行时选择不同算法的场景,算法之间是独立且可互相替换的。
4. 总结
状态模式和策略模式都是行为设计模式,它们通过将不同的行为封装在独立的类中,提供了解耦和扩展的优雅解决方案。状态模式适用于需要处理对象状态转换的场景,而策略模式适用于需要在不同算法之间进行选择的场景。
在C++中,使用状态模式和策略模式都可以有效地提升代码的灵活性和可维护性,通过抽象类和多态性,实现行为的动态替换。