本文为Head First设计模式笔记。
策略模式是将应用中实现方法多变的算法独立出来,封装为算法族,通过组合或者其他方式,让算法的调用切换独立于算法的调用者。
我们要设计一个duck鸭子类。我们首先想到设计一个duck基类,然后派生出各种各样的鸭子。
class Duck{virtual void display();virtual void swim() { std::cout <<"swim";}
};class GreenDuck:public Duck{void display(){ std::cout <<"GreenDuck";}
};class RedDuck:public Duck{void display(){ std::cout <<"RedDuck";}
};
这样设计看似毫无问题,而当我们需要添加fly()飞行属性时,却发现,有的鸭子会飞,有的鸭子却不会飞,不能在基类中,直接定义fly()函数。如果我们使用接口来让会飞的鸭子实现接口,不会飞的鸭子没有接口,这样看似没有问题。但是我们如果有几百种会飞的鸭子,将要把飞行实现几百遍,并且我们还要为每一种鸭子检查接口。因此使用接口并不是一个良好的解决方案。接下来,我们将利用设计模式的思想,优化类的实现。
1、找出应用中可能需要变化之处,将它们独立出来,不要和不变的代码混在一起。
对于fly()函数来说,它是类中的变化部分。让它与基类揉搓在一起,明显不是明智之举,因此我们要把它独立出来,单独封装为一个类。
2、针对接口编程,而不是针对实现
在以往的做法中,我们会将fly等行为声明在基类或者接口,让子类继承,并由子类实现。这两种做法都依赖于“实现”,函数需要在子类中进行定义,函数的实现都与子类的实现相捆绑。
而我们可以将fly独立为一个全新的flyBehavior类,fly通过继承flyBehavior,完成不同的实现,fly动作的实现和Duck子类的实现完全脱离。所以,函数的实现可以与子类的实现相脱离。
3、多用组合,少用继承
在Duck类中,我们可以声明一个flyBehavior类对象。利用组合的方式,将fly动作添加到Duck类中,而不是采取继承的老路,让fly与Duck相捆绑。
class FlyBehavior{
public:virtual void fly() = 0;
};class CanFly:public FlyBehavior{void fly(){ std::cout <<"can Fly";}
};class CanontFly:public FlyBehavior{void fly(){ std::cout <<"cannnot fly";}
};
class Duck{
public:FlyBehavior* flyType;virtual void display() = 0;virtual void swim() { std::cout <<"swim";}void Duckfly() {flyType -> fly();}
};class GreenDuck:public Duck{
public:GreenDuck(){flyType = new CanFly();}void display(){ std::cout <<"GreenDuck";}~GreenDuck(){delete flyType;}
};
从实现可以看出,变化的fly算法被独立出来实现,与算法的调用者Duck实现分开。策略模式可以使我们的代码更加易于维护。