设计模式 -- 策略模式(Strategy Pattern)

1 问题引出

1.1 鸭子项目

  1. 有各种鸭子(比如 野鸭、北京鸭、水鸭等, 鸭子有各种行为,比如 叫、飞行等)

  2. 显示鸭子的信息

1.2 传统方式

设计模式 -- 策略模式(Strategy Pattern)_第1张图片

  1. 其它鸭子,都继承了 Duck 类,所以 fly 让所有子类都会飞了,这是不正确的

  2. 上面说的 1 的问题,其实是继承带来的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分。会有溢出效应

  3. 为了改进 1 问题,我们可以通过覆盖 fly 方法来解决 => 覆盖解决

  4. 问题又来了,如果我们有一个玩具鸭子 ToyDuck, 这样就需要 ToyDuck 去覆盖 Duck 的所有实现的方法 => 解决思路 -》 策略模式 (strategy pattern)

2 基本介绍

  1. 策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
  2. 这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)
  3. 策略模式是一种行为设计模式,使你能在运行时改变对象的行为

3 原理结构图

3.1 类图

设计模式 -- 策略模式(Strategy Pattern)_第2张图片

3.2 说明

  1. 抽象策略类(Strategy):定义了一个表示各种策略的接口或抽象类,规定了具体策略类必须实现的方法。
  2. 具体策略类(ConcreteStrategyX):实现了抽象策略类定义的接口或抽象类,包含具体的算法实现。
  3. 上下文类(Context):维护一个对策略对象的引用,并通过该引用调用策略的方法。

4 应用实例

4.1 类图

设计模式 -- 策略模式(Strategy Pattern)_第3张图片

4.2 代码实现Client

public class Client {
​
    public static void main(String[] args) {
        WildDuck wildDuck = new WildDuck();
        wildDuck.fly();
​
        ToyDuck toyDuck = new ToyDuck();
        toyDuck.fly();
​
        PekingDuck pekingDuck = new PekingDuck();
        pekingDuck.fly();
​
        //  动态改变某个对象的行为, 北京鸭 不能飞
        pekingDuck.setFlyBehavior(new NoFlyBehavior());
        System.out.println("北京鸭的实际飞翔能力");
​
        pekingDuck.fly();
    }
​
}

Duck

public abstract class Duck {
​
    //  属性, 策略接口
    FlyBehavior flyBehavior;
    //  其它属性<->策略接口
    QuackBehavior quackBehavior;
​
    public Duck() {
​
    }
​
    public abstract void display();//  显示鸭子信息
​
    public void quack() {
        System.out.println("鸭子嘎嘎叫~~");
    }
​
    public void swim() {
        System.out.println("鸭子会游泳~~");
​
    }
​
    public void fly() {
        //  改进
        if (flyBehavior != null) {
            flyBehavior.fly();
        }
    }
​
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }
​
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }
​
}

PekingDuck

public class PekingDuck extends Duck {
​
    //  假如北京鸭可以飞翔,但是飞翔技术一般
    public PekingDuck() {
        flyBehavior = new BadFlyBehavior();
    }
​
    @Override
    public void display() {
        System.out.println("~~北京鸭~~~");
    }
}

FlyBehavior

public interface FlyBehavior {
​
    void fly(); //  子类具体实现
}

NoFlyBehavior

public class NoFlyBehavior implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println(" 不会飞翔   ");
    }
}

GoodFlyBehavior

public class GoodFlyBehavior implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println(" 飞翔技术高超 ~~~");
    }
}

BadFlyBehavio

public class BadFlyBehavior implements FlyBehavior {
​
    @Override
    public void fly() {
        System.out.println(" 飞翔技术一般 ");
    }
​
}

QuackBehavior

public interface QuackBehavior { 
    void quack();// 子类实现
}

5 优缺点

5.1 优点

  1. 符合开闭原则:策略模式符合开闭原则,即对扩展开放,对修改关闭。当需要添加新策略时,只需添加一个新的策略类而无需修改现有代码。
  2. 易于理解和维护:通过将不同的算法或行为封装在不同的策略类中,避免了在主代码中使用复杂的if-else或switch-case语句,从而使得代码更加清晰和易于维护。
  3. 符合单一职责:每个策略类只处理一种算法,符合单一职责原则,使得各个类的职责分明且互相独立。
  4. 减少依赖:策略对象之间没有依赖关系,修改一个策略不会影响其他策略,降低了耦合度。
  5. 共享行为:策略模式允许将多个策略的共同行为放在抽象策略基类中,减少了代码冗余,并提高了复用性。

5.2 缺点

  1. 学习成本较高:客户端必须了解所有策略并自行决定使用哪个策略,这增加了客户端的使用复杂度。如果策略数量很多,不仅会增加对象的数目,还会使得客户端在选择合适策略时面临更多的决策困难。
  2. 增加复杂性:随着策略类的增加,系统中类的总数也会增加,这会提高系统的复杂性和维护难度。
  3. 资源消耗:更多的策略类意味着更高的资源消耗,包括内存和CPU的占用。
  4. 策略切换开销:频繁切换策略可能会引入额外的性能开销,特别是在需要频繁更改策略的系统中。

6 应用场景

  1. 促销策略:商场促销活动中,不同的促销策略如满减、返现、打折等可以被封装在不同的策略类中。
  2. 算法选择:在图像处理软件中,对图片的不同处理方式如灰度处理等可以封装在不同的策略类中。
  3. 数据排序:在排序算法中,如冒泡排序、快速排序、归并排序等可以被封装在不同的策略类中。
  4. 交通导航:导航软件中的路线选择策略如最短路线、最快路线、最少收费路线等可以封装在不同的策略类中。

7 总结

        综上所述,策略模式提供了一种有效的方式来分离算法的选择和使用,使得算法可以在使用过程中动态切换,从而提高了系统的灵活性和可维护性。然而,这种灵活性也带来了一定的复杂性和维护成本,因此在使用策略模式时,需要综合考虑其优缺点,结合具体应用场景进行合理应用。

你可能感兴趣的:(设计模式,设计模式,策略模式)