策略模式
策略模式将可变的部分从程序中抽象分离成算法接口,在该接口下分别封装一系列算法实现,
并使得他们可以相互替换,从而导致客户端程序独立于算法的改变。
实例:
假如有一个鸭子类:鸭子有叫的功能,有展示功能,另外需要给鸭子加一个飞行能力,让鸭子飞起来,但是对于不同的鸭子有不同的飞行能力,
比如有的展翅高飞,有的不会飞,有的飞向太空。
方案一:继承:
在父类中提供实现方法,子类通过继承获得父类中的飞行行为。
优点:简单易用,已有应用可以快速添加飞行能力。
缺点:不具有灵活性,对未来变更支持性差。假如子类鸭子有跟父类不一样的飞行方法,需要在子类中复写飞行的方法以提供新的
飞行行为,这很容易出差造成错误(粗心的程序员忘记复写),假如一只大黄鸭,是不具备飞行能力的,但是因为程序员忘记复写飞行,导致该大黄鸭能飞。
方案二:抽象方法
在父类中提供抽象方法,强迫子类实现自己的飞行行为。
优点:足够灵活,小伙伴再也不会忘记复写代码了,大黄鸭也不会飞到天上吓你了。
缺点:累死小伙伴,每个子类都要实现一遍代码,即使是相同的行为也不例外。
代码重复,却没有复用代码。万一再有一个bug,哎妈呀,不敢想。
继承是重用代码的利器,但是继承并不总是最好的工具。
解决思想:复合优先于继承,多用组合,少用继承。
组合:在类中增加一个私有域,引用另一个已有的类的实例,通过调用引用实例的方法从而获得新的功能,
这种设计被称为组合(复用)。
就像赛车通过换轮胎,获得超凡奔跑能力。
方案三:组合
将飞行行为抽象为接口,在父类中持有该接口,并由该接口代理飞行行为。
public interface FlyingStragety { void performFly(); }
父类:
private FlyingStragety flyingStragety; public void fly() { flyingStragety.performFly(); }
优点:足够灵活,复用代码,更易于维护。
策略模式的设计原则:
1,找出应用中需要变化的部分,把他们独立出来,不要和那些不需要的代码混在一起(可复用的功能独立出来,供不同的子类重用,这也是为什么不采用抽象的原因)。
2,面向接口编程,而不是面向实现编程。
3,多用组合,少用继承。
策略模式的实现:
1,通过分离变化得出的策略接口Stratege。(策略接口,实现鸭子的飞行行为)。
2,继承策略模式接口,实现Stratege的实现类(不同的飞行行为,分别实现实现类)。
3,策略模式的实现:客户程序有一个"Stratege接口对象"。(在父类抽象类中实现即可)
4,在客户程序中选择/组装正确的Stratege实现。(在stratege对象的set方法里面,传入要选择的具体策略类实例)。
具体代码:
1,先定义一个飞行策略接口,该接口提供一个飞行方法。
package com.Stratege; /* * 策略接口,实现鸭子的飞行行为 * */ public interface FlyingStragety { void performFly(); }
2,对于飞行接口,分别提供几种飞行实现类,来供客户程序选择使用哪个策略Stratege实现。
如:不会飞实现类:
package com.Stratege.impl; import com.Stratege.FlyingStragety; //不能飞行 public class FlyNoWay implements FlyingStragety { @Override public void performFly() { System.out.println("我不会飞行,有点羞涩"); } }
展翅高飞实现类:
package com.Stratege.impl; import com.Stratege.FlyingStragety; //第一种飞行策略:展翅高飞 public class FlyWithMe implements FlyingStragety { @Override public void performFly() { System.out.println("振翅高飞"); } }
3,不同的飞行策略的接口和实现类已经实现了。
现在开始实现客户端类,假如有三种鸟:绿头鸭,红头鸭,橡胶鸭,小黄鸭。其中绿头鸟和红头鸭会飞,
橡胶鸭和小黄鸭不会飞,橡皮鸭子叫声有点沙哑:嘎~嘎~嘎~。
4,定义一个会叫,能展示的鸭子,不同的鸭子展示方式不一样,这里通过一个抽象方法实现,并且采用策略模式提供飞行功能。
策略模式飞行:1,提供策略模式飞行接口对象,2,提供该接口对象的set方法。
3,提供飞行方法,方法里面由策略模式接口对象调用自身的performFly。这样实现了,不同的子类,调用飞行方法,不需要修改飞行方法,
只需要为给策略模式飞行接口对象实例化不同的策略模式实现类。
package com.Stratege; /* * 超类,所有的鸭子都要继承此类 * 抽象了鸭子的行为:显示和叫 * */ public abstract class Duck { //鸭子发出叫声 //通用行为,由超类实现 public void quack() { System.out.println("嘎嘎嘎"); } //显示鸭子的外观,鸭子外观各不相同,声明为abstract,由子类实现 public abstract void display(); private FlyingStragety flyingStragety; public void setFlyingStragety(FlyingStragety flyingStragety) { this.flyingStragety = flyingStragety; } //调用策略接口实现飞行 public void fly() { this.flyingStragety.performFly(); } }
5,不同的鸭子子类分别实例化:在构造方法里面,调用父类的setStragety方法,方法的参数传递对应的实现子类实例,从而实现往子类中注入不同的策略实例,
绿鸭:
package com.Stratege; import com.Stratege.impl.FlyWithMe; public class MallardDuck extends Duck { public MallardDuck() { super(); //给红头鸭提供了一个翅膀飞行的能力 super.setFlyingStragety(new FlyWithMe()); } @Override public void display() { System.out.println("我的头是绿色的"); } }
红头鸭:
package com.Stratege; import com.Stratege.impl.FlyWithMe; public class RedheadDuck extends Duck { public RedheadDuck() { super(); //给红头鸭提供了一个翅膀飞行的能力 super.setFlyingStragety(new FlyWithMe()); } @Override public void display() { System.out.println("我的头是红色的"); } }
橡胶鸭:因为叫声不一样,重写了叫声方法。
package com.Stratege; import com.Stratege.impl.FlyNoWay; public class RubberDuck extends Duck{ public RubberDuck() { super(); //给红头鸭提供了一个翅膀飞行的能力 super.setFlyingStragety(new FlyNoWay()); } @Override public void display() { System.out.println("我全身发黄,嘴巴很红"); } public void quack() { System.out.println("嘎~嘎~嘎~"); } }
大黄鸭:不会飞。
package com.Stratege; import com.Stratege.impl.FlyNoWay; public class BigYellow extends Duck { public BigYellow() { super(); //给红头鸭提供了一个翅膀飞行的能力 super.setFlyingStragety(new FlyNoWay()); } @Override public void display() { System.out.println("我身体很大,全身黄黄"); } }
测试:
package com.Stratege; public class DuckTest { public static void main(String[] args) { System.out.println("测试鸭子程序"); System.out.println("-----------------"); Duck duck=new MallardDuck(); duck.display(); duck.quack(); duck.fly(); System.out.println("-----------------"); Duck duck2=new RedheadDuck(); duck2.display(); duck2.quack(); duck2.fly(); System.out.println("-----------------"); Duck duck3=new RubberDuck(); duck3.display(); duck3.quack(); duck3.fly(); System.out.println("-----------------"); Duck duck4=new BigYellow(); duck4.display(); duck4.quack(); duck4.fly(); } }
运行结果:
测试鸭子程序 ----------------- 我的头是绿色的 嘎嘎嘎 振翅高飞 ----------------- 我的头是红色的 嘎嘎嘎 振翅高飞 ----------------- 我全身发黄,嘴巴很红 嘎~嘎~嘎~ 我不会飞行,有点羞涩 ----------------- 我身体很大,全身黄黄 嘎嘎嘎 我不会飞行,有点羞涩
总结:
策略模式的优点:
1,使用了组合模式,使得架构更加灵活。
2,富有弹性,可以较好的应对变化(开-闭原则)。
3,更好的代码复用性。(相对于继承。而且如果相同的策略,只需要实例化对应的策略模式类就行了)
4,消除大量的条件语句。(不同的子类,当对于不同的策略,只需要为策略接口对象,实例化对应的策略模式类就行了)
适用场景:
1,许多相关的类,仅仅是行为差异。
2,运行时选取不同的算法变体。
3,通过条件语句在多个分支中选取一。