目录
必备背景知识
一.使用场景
二.核心逻辑
三.举例子
总结
必备背景知识
- 针对接口编程,而不是针对实现编程(修改功能更加容易,我们只需要对接口作出修改即可)
- 优先使用组合而不是继承(继承会导致后来的子类都具备了父类的功能,有时候我们想让子类没有父类的某些功能)
一.使用场景
由当前对象所拓展出来的对象有可能有新增功能,或者不具备原来某些功能的时候可以使用。优点在于可拓展性高,维护便捷,能动态的改变对象的属性。若想给所有同一大类但不同属性的不同对象增加一个新的公用功能,直接提供接口即可。不用依次修改。
二.核心逻辑
将不同对象反复变化的部分抽象封装出来,后序修改时只针对变化的部分进行修改这样不会影响其他不变化的部分。
三.举例子
从代码理解好理解一些。假设我们有个基类鸭子,我想拥有不同种类的鸭子子类。不同鸭子拥有不同的行为。有的会叫有的不会叫。我们统一将这些变化的属性抽象出来并单独成为一个行为类。同时基类提供接口来获取这些行为对象。同时针对这个行为,我们可以实现多种不同的实现方法类。这样我们通过子类继承基类,并在子类内特化这些行为对象选择对应的方法类就能动态的实现不同鸭子的不同行为了。把行为的决定权下放到子类而不是基类决定。基类只负责提供获取接口。
首先我们看基类duck
class Duck
{
public:std::unique_ptr<FlyBehavior> flyBehavior; //两个行为对象,由不同子类对象特化具体行为std::unique_ptr<QuackBehavior> quackBehavior;Duck(std::unique_ptr<FlyBehavior> flyBehavior, std::unique_ptr<QuackBehavior> quackBehavior); //这里采取将行为类在构造函数内传递的方式,不同的鸭子将不同方法传给鸭子基类以实现构造virtual ~Duck() = default; //析构函数虚继承为了正确释放void performQuack() const //父类提供接口给用户,用户直接调取对应鸭子的状态{quackBehavior->quack(); //调用行为类内的接口,具体实现由行为类下的实现方法类完成}virtual void display() const = 0; //用于测试或实现鸭子具体功能void performFly() const{flyBehavior->fly();}
};
这样我们就需要提供对应的行为类。也就是飞行行为和叫行为。我们需要在两种行为类内实现接口提供给后来的行为的实现类使用,并且保持一致。同时让鸭子的基类提供给用户使用的接口函数调用这个接口,就能直接获取到不同的行为实现方法。
这里以飞行行为为例
class FlyBehavior {
public:virtual void fly() const = 0; //后序实现所有有关飞行行为的实现方法都要提供fly的实现virtual ~FlyBehavior() = default;
};
这样我们就可以实现关于飞行的多种不同实现方式。包括但不限于用翅膀飞或者完全不能飞。
class FlyNoWay: public FlyBehavior {
public: void fly() const {std::cout << "I can't fly!" << std::endl; //实现不会飞的鸭子}
};class FlyWithWings: public FlyBehavior {
public:virtual void fly() const {std::cout << "I'm flying!" << std::endl; //实现用翅膀飞的鸭子}
};
到目前为止大体框架已经完成了。若想添加动态设置功能。我们可以不用智能指针并且在duck的基类内添加set接口,将参数设置为行为方式,将接受到的行为方式复制给当前鸭子内的行为方式即可。
总结
策略模式定义了一个算方族,分辨封装,使得相互之间可以互相转化。让各个模块独立。