鸭子问题
1.有各种各样的鸭子,eg:野鸭,北京鸭,水鸭
2.鸭子有各种行为:叫、飞行
3.显示鸭子信息
传统方案
Duck
野鸭
北京鸭
水鸭
传统的方式实现的问题分析和解决方案
1.其它鸭子,都继承了 Duck 类,所以 fly 让所有子类都会飞了,这是不正确的
2.上面说的1的问题,其实是继承带来的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分,容易引起溢出效应。
3.为了改进1问题,我们可以通过覆盖fly方法来解决 => 覆盖解决
策略模式
定义算法族,分别封装放入独立的类中,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组 合 聚合,少用继承(客户通过组合方式使用策略)
策略模式结构

1.上下文 (Context)维护指向具体策略的引用, 且仅通过策略接口与该对象进行交流。
2.策略 (Strategy) 接口是所有具体策略的通用接口, 它声明了一个上下文用于执行策略的方法。
3.具体策略 (Concrete Strategies) 实现了上下文所用算法的各种不同变体。
4.客户端 (Client)会创建一个特定策略对象并将其传递给上下文。 上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。
当上下文需要运行算法时, 它会在其已连接的策略对象上调用执行方法。 上下文不清楚其所涉及的策略类型与算法的执行方式。
策略模式适合应用场景
1.当你想使用对象中各种不同的算法变体, 并希望能在运行时切换算法时, 可使用策略模式。
2.当你有许多仅在执行某些行为时略有不同的相似类时, 可使用策略模式。
3.如果算法在上下文的逻辑中不是特别重要, 使用该模式能将类的业务逻辑与其算法实现细节隔离开来。
4.当类中使用了复杂条件运算符以在同一算法的不同变体中切换时, 可使用该模式。
实现方式
- 从上下文类中找出修改频率较高的算法 (也可能是用于在运行时选择某个算法变体的复杂条件运算符)。
- 声明该算法所有变体的通用策略接口。
- 将算法逐一抽取到各自的类中, 它们都必须实现策略接口。
- 在上下文类中添加一个成员变量用于保存对于策略对象的引用。 然后提供设置器以修改该成员变量。 上下文仅可通过策略接口同策略对象进行交互, 如有需要还可定义一个接口来让策略访问其数据。
- 客户端必须将上下文类与相应策略进行关联, 使上下文可以预期的方式完成其主要工作。
策略模式优缺点
优点:
✔️ 你可以在运行时切换对象内的算法。
✔️ 你可以将算法的实现和使用算法的代码隔离开来。
✔️ 你可以使用组合来代替继承。
✔️ 开闭原则。 你无需对上下文进行修改就能够引入新的策略。
缺点:
❌ 如果你的算法极少发生改变, 那么没有任何理由引入新的类和接口。 使用该模式只会让程序过于复杂。
❌ 客户端必须知晓策略间的不同——它需要选择合适的策略。
❌ 许多现代编程语言支持函数类型功能, 允许你在一组匿名函数中实现不同版本的算法。 这样, 你使用这些函数的方式就和使用策略对象时完全相同, 无需借助额外的类和接口来保持代码简洁。
解决鸭子问题

代码
//飞行接口
public interface FlyBehavior {
//飞行方法
void fly();
}
public class BadFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println("飞翔技术一般");
}
}
public class GoodFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("飞翔技术高超");
}
}
public class NoFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println("不会飞翔");
}
}
public interface QuackBehavior {
void quack();
}
public class GaGaQuackBehavior implements QuackBehavior {
@Override
public void quack() {
System.out.println("Ga~Ga~");
}
}
public class NoQuackBehavior implements QuackBehavior{
@Override
public void quack() {
System.out.println("该鸭子不会叫");
}
}
public abstract class Duck {
//策略接口
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public abstract void display();//显示鸭子信息
public void quack(){
quackBehavior.quack();
}
public void fly(){
if (flyBehavior != null)
flyBehavior.fly();
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
//野鸭
public class WildDuck extends Duck{
public WildDuck() {
//飞行能力好,GaGa叫
flyBehavior = new GoodFlyBehavior();
quackBehavior = new GaGaQuackBehavior();
}
@Override
public void display() {
System.out.println("野鸭");
}
}
//玩具鸭
public class ToyDuck extends Duck{
public ToyDuck() {
//不会飞、不会叫
flyBehavior = new NoFlyBehavior();
quackBehavior = new NoQuackBehavior();
}
@Override
public void display() {
System.out.println("玩具鸭");
}
}
//北京鸭
public class PekingDuck extends Duck{
public PekingDuck(){
//飞行能力一般,GaGa叫
flyBehavior = new BadFlyBehavior();
quackBehavior = new GaGaQuackBehavior();
}
@Override
public void display() {
System.out.println("北京鸭");
}
}
public class Client {
public static void main(String[] args) {
Duck wildDuck = new WildDuck();
wildDuck.display();
wildDuck.fly();
wildDuck.quack();
Duck toyDuck = new ToyDuck();
toyDuck.display();
toyDuck.fly();
toyDuck.quack();
}
}
策略模式的注意事项和细节
1.策略模式的关键是: 分析项目中变化部分与不变部分
2.策略模式的核心思想是:多用组合聚合,少用继承;用行为类组合,而不是行为的继承。更有弹性
3.体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可避免了使用多重转移语句( if…else if…else)
4.提供了可以替换继承关系的办法策略模式将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展
5.需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大
5686





