【偷学设计模式之结构型模式】策略 Strategy。无限拓展?开闭原则?这样的设计谁不爱呢?!!

366 阅读4分钟

策略 Strategy 定义

定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。

这个定义比较抽象,是我抄来的,说了半天,好像很强,但又不知所云。下面我们用具体的例子来做具象化的了解。

如何理解

说到策略,一定离不开接口,其核心就是要对我们的策略进行抽象抽象成一个策略接口,然后基于策略接口进行不同类型的策略实现,最后需要一个承载运行策略的一个环境

那么其实核心可以分成 3 部分

1. 策略抽象接口 2. 策略抽象接口实现类 3. 策略运行承载类

如此设计的好处就是,抽象接口不用修改运行承载类不用修改,我们只需要拓展策略接口的实现类即可。满足开闭原则

举个例子

给出一个攻击的抽象策略接口

@FunctionalInterface
public interface Attack<T> {
    void doAttack(T t1,T t2);
}

定义一个英雄实体,包含一些基本属性

import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Hero {

    private String name;
    /**
     * 攻击力
     */
    private Integer AP;

    /**
     * 生命值
     */
    private Integer HP;

    /**
     * 法力值
     */
    private Integer MP;
}

定义一个战场环境,作为承载执行攻击策略的载体。

public class Battlefield<T> {

    public void fight(Attack<T> attack, T t1, T t2) {
        System.out.println();
        System.out.println("初始 战斗平台");
        System.out.println(t1+"进入战场");
        System.out.println(t2+"进入战场");
        attack.doAttack(t1, t2);
        System.out.println("初始 攻击结束");
        System.out.println();
    }
}

抽象策略的实现 其实就有很多种方式了:

  1. 定义不同的类继承实现
  2. 在调用的时候直接内部实现
  3. 函数式编程
public class Strategy {

    public static void main(String[] args) {
        Battlefield battlefield =new Battlefield();
        Hero hero1=new Hero("盖伦",4,10,30);
        Hero hero2=new Hero("诺手",2,50,90);
        battlefield.fight((Attack<Hero>) (t1, t2) -> System.out.println(t1.getName()+" 对 "+t2.getName()+" 使用 旋风斩 造成了 "+t1.getAP()+" 点伤害"),hero1,hero2);

        battlefield.fight((Attack<Hero>) (t1, t2) -> System.out.println(t1.getName()+" 对 "+t2.getName()+" 使用 正义审判 造成了 "+t1.getAP()*10+" 点伤害"),hero1,hero2);

        battlefield.fight((Attack<Hero>) (t2, t1) -> System.out.println(t2.getName()+" 对 "+t1.getName()+" 使用 无情铁手 造成了 "+t2.getAP()*3+" 点伤害"),hero2,hero1);
    }
}

博主这里使用函数式编程的方式,实现了 3 种不同的策略,最终的运行效果如下: 在这里插入图片描述

如何运用

优点:
  1. 使程序更易维护。避免了多 case 情况的大量 if else 语句,
  2. 符合开闭原则。可以在不修改原有代码的情况下灵活添加算法。
  3. 高内聚低耦合。将算法的使用放到环境类中,算法的实现放到策略接口实现中。
缺点:
  1. 可能会造成大量的策略接口实现类
  2. 使用者必须清楚何时使用恰当的策略类
运用:

在运用上,我们需要确定当前场景是否符合使用策略模式的条件:

  1. 是否有策略(某些算法或者方法)可以进行抽象
  2. 是否有必要抽象,抽象后是否经常需要拓展

在必要的时候使用策略模式是一种设计的美,但并不是任何场景都需要套用策略模式,不加思考的套用可能反而增加程序的维护成本。

JDK 对策略模式的应用

那么 JDK 在设计上也使用了策略模式。 比如 Comparator 接口,比较器接口就是典型的策略接口,这使得我们可以通过该接口定义任意的比较规则。 Arrays.sort() 就是具体比较策略算法的使用承载环境类,对你提供的集合和比较算法进行排序。 这里比较策略就是我们可以灵活拓展定制的部分了。

总结

对于 策略模式 你只需要牢牢记住 3 部分就好了。这里我再重复一遍:

1. 策略抽象接口 2. 策略抽象接口实现类 3. 策略运行承载类

在实际开发中灵活思考进行运用。但我们仍需记住一点,只在必要的时候使用,而不是生搬硬套的炫技。

希望本文对你有所帮助,同时 欢迎添加个人微信 dyinggq 一起交流学习~~

我是 dying 搁浅 ,我始终期待与你的相遇。无论你是否期待,潮涨潮落,我仅且就在这里…

我们下期再见~