这个文章只是自己初学设计模式的一些所感和所悟,如果有什么不足或者错误的地方,希望各位能够提出宝贵的意见。
一、含义
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。
分析下定义,策略模式定义和封装了一系列的算法,它们是可以相互替换的,也就是说它们具有共性,而它们的共性就体现在策略接口的行为上,另外为了达到目的,也就是说让算法独立于使用它的客户而独立变化,我们需要让客户端依赖于策略接口。
二、举例
场景:
有一套模拟鸭子的游戏。游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫。游戏采用了标准OO技术,设计了一个鸭子超类Duck,并让各种鸭子继承此超类。
现在需要为鸭子添加新的行为Fly。如果在SuperClass中添加Fly则会使所有的鸭子对象都具有该方法。但实际中有些鸭子(例如橡皮鸭)是不会飞的。这种情况下只能在橡皮鸭子类中覆盖Fly。这无疑比较丑陋。
************************************************************************************
需要解决的问题:
- 代码在多个子类中重复
- 运行时行为不容易改变
- 很难知道所有鸭子的行为
- 改变会牵一发而动全身,造成其他鸭子不想要的改变
从上面这张图就可以看出来,在基类中新家的方法并不是所有鸭子想要的。一直在基类中加方法会使得扩展性极差。
*****************************************************************************
对策:
策略一:将fly和quack两个行为从父类抽离出来,放到接口中,这样让会飞会叫的鸭子去实现该接口,而对于诱饵鸭这种不会飞不会叫的鸭子则不去实现接口。
策略二:都有飞和叫两个接口,但与策略一不同的是还有它们具体的类,负责实现具体的行为
选择策略二,因为如果是单纯使用接口,每个会飞的子类都需要实现Fly接口,会出现大量重复代码,没有做到代码复用。
策略二的使用:
整体格局:
回顾策略模式的定义:
定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于实用算法的客户。
这个例子中封装的算法族是鸭子叫和鸭子飞的一系列算法。通过继承接口,使得可以具体实现不同的方法。
***************************************************************
具体代码实现:(注意以下代码是一个整体,这里只是为了方便看,用*隔开了)
**************************************************************
//超类
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){};
public abstract void display();
public void performFly()
{
flyBehavior.fly();
}
public void performQuack()
{
quackBehavior.quack();
}
//动态设定飞行行为
public void setFlyBehavior(FlyBehavior fb)
{
flyBehavior = fb;
}
//动态设定鸣叫行为
public void setQuackBehavior(QuackBehavior qb)
{
quackBehavior = qb;
}
public void swim()
{
System.out.println("All ducks float, even decoys");
}
}
*******************************************************************
//飞行接口
public interface FlyBehavior {
public void fly();
}
//鸣叫接口
public interface QuackBehavior {
public void quack();
}
*********************************************************************
具体行为的实现:
//翅膀飞行类
public class FlyWithWings implements FlyBehavior {
public void fly(){
System.out.println("i am flying");
}
}
//不会飞类
public class FlyNoWay implements FlyBehavior {
public void fly(){
System.out.println("I can not fly");
}
}
//鸭叫类
public class Quack implements QuackBehavior {
public void quack(){
System.out.println("I am quacking");
}
}
//不能叫类
public class MuteQuack implements QuackBehavior{
public void quack(){
System.out.println("I can not quack");
}
}
//吱吱叫类
public class Squeak implements QuackBehavior{
public void quack(){
System.out.println("squeak");
}
}
**************************************************************
//野鸭类
public class MallardDuck extends Duck {
public MallardDuck(){
quackBehavior = new Quack();//注意这里,根据不同的鸭子,new不同的行为
flyBehavior = new FlyWithWings();//注意这里,根据不同的鸭子,new不同的行为
}
public void display(){
System.out.println("i am a real mallardduck");
}
}
//测试类
public class MiniDuckSimulator {
public static void main(String[] args){
Duck mallard = new MallardDuck();
if (mallard == null){
System.out.println("mallard is null");
}
mallard.performFly();
mallard.performQuack();
}
}
*******************************************************************
总结:
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
****************************************************************************************
参考资料:《headfirst设计模式》
参考博客:
https://blog.csdn.net/guojunxiu/article/details/81902604
https://wangcw.blog.csdn.net/article/details/80431481
https://blog.csdn.net/JavaCoder_juejue/article/details/85221608