JYM 设计模式系列- 策略模式,模板方法模式,让你的代码更优雅!!!

2,748 阅读6分钟

觉得不错请按下图操作,掘友们,哈哈哈!!! image.png

概述设计模式分类

总体来说设计模式分为三大类:

  • 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

  • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

一:策略模式

Strategy 模式(也称为策略模式)是一种软件设计模式,允许在运行时选择算法的行为。在 Java 8 之前,Strategies 需要是单独的类,迫使开发人员编写大量样板代码。使用现代 Java,很容易通过方法引用和 lambda 传递行为,从而使代码更短、更易读。

在这个例子中 DragonSlayingStrategy 封装了一个算法。包含对象 DragonSlayer(屠龙者) 可以通过改变它的策略来改变它的行为。

1.1 使用场景

  • 针对同一类型的问题多种处理
  • 需要安全的封装多种同一类型的操作,对客户隐藏具体实现。
  • 出现同一抽象类有多个子类,而又需要使用 if-else 或者switch-case 来选择具体子类时。

image.png

1.2 优缺点

优点

  • 结构清晰明了,使用简单直观
  • 耦合度相对较低,扩展方便
  • 操作封装更彻底,数据更加安全

缺点

  • 随着策略的增加,类 会变得越来越多,容易造成类爆炸
  • 使用者必须知道所有策略类,并自行解决使用哪个策略

1.3 直接上demo

策略接口:

@FunctionalInterface
public interface DragonSlayingStrategy {

  void execute();

}

DragonSlayer:屠龙者使用不同的策略来屠龙

public class DragonSlayer {

  private DragonSlayingStrategy strategy;

  public DragonSlayer(DragonSlayingStrategy strategy) {
    this.strategy = strategy;
  }

  public void changeStrategy(DragonSlayingStrategy strategy) {
    this.strategy = strategy;
  }

  public void goToBattle() {
    strategy.execute();
  }
}

枚举策略模式的 Lambda 实现:

@Slf4j
public class LambdaStrategy {

  /**
   * Enum to demonstrate strategy pattern.
   */
  public enum Strategy implements DragonSlayingStrategy {
    MeleeStrategy(() -> LOGGER.info(
        "With your Excalibur you severe the dragon's head!")),
    ProjectileStrategy(() -> LOGGER.info(
        "You shoot the dragon with the magical crossbow and it falls dead on the ground!")),
    SpellStrategy(() -> LOGGER.info(
        "You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"));

    private final DragonSlayingStrategy dragonSlayingStrategy;

    Strategy(DragonSlayingStrategy dragonSlayingStrategy) {
      this.dragonSlayingStrategy = dragonSlayingStrategy;
    }

    @Override
    public void execute() {
      dragonSlayingStrategy.execute();
    }
  }
}

近战策略:

@Slf4j
public class MeleeStrategy implements DragonSlayingStrategy {

  @Override
  public void execute() {
    LOGGER.info("With your Excalibur you sever the dragon's head!");
  }
}

弹丸策略:

@Slf4j
public class ProjectileStrategy implements DragonSlayingStrategy {

  @Override
  public void execute() {
    LOGGER.info("You shoot the dragon with the magical crossbow and it falls dead on the ground!");
  }
}

法术策略:

@Slf4j
public class SpellStrategy implements DragonSlayingStrategy {

  @Override
  public void execute() {
    LOGGER.info("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!");
  }

}

程序入口:

@Slf4j
public class App {

  private static final String RED_DRAGON_EMERGES = "Red dragon emerges.";
  private static final String GREEN_DRAGON_SPOTTED = "Green dragon spotted ahead!";
  private static final String BLACK_DRAGON_LANDS = "Black dragon lands before you.";

  /**
   * Program entry point.
   *
   * @param args command line args
   */
  public static void main(String[] args) {
    //GoF 策略模式
    LOGGER.info(GREEN_DRAGON_SPOTTED);
    var dragonSlayer = new DragonSlayer(new MeleeStrategy());
    dragonSlayer.goToBattle();
    LOGGER.info(RED_DRAGON_EMERGES);
    dragonSlayer.changeStrategy(new ProjectileStrategy());
    dragonSlayer.goToBattle();
    LOGGER.info(BLACK_DRAGON_LANDS);
    dragonSlayer.changeStrategy(new SpellStrategy());
    dragonSlayer.goToBattle();

    // Java 8 函数式实现策略模式
    LOGGER.info(GREEN_DRAGON_SPOTTED);
    dragonSlayer = new DragonSlayer(
        () -> LOGGER.info("With your Excalibur you severe the dragon's head!"));
    dragonSlayer.goToBattle();
    LOGGER.info(RED_DRAGON_EMERGES);
    dragonSlayer.changeStrategy(() -> LOGGER.info(
        "You shoot the dragon with the magical crossbow and it falls dead on the ground!"));
    dragonSlayer.goToBattle();
    LOGGER.info(BLACK_DRAGON_LANDS);
    dragonSlayer.changeStrategy(() -> LOGGER.info(
        "You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"));
    dragonSlayer.goToBattle();

    // 具有枚举策略模式的 Java 8 lambda 实现
    LOGGER.info(GREEN_DRAGON_SPOTTED);
    dragonSlayer.changeStrategy(LambdaStrategy.Strategy.MeleeStrategy);
    dragonSlayer.goToBattle();
    LOGGER.info(RED_DRAGON_EMERGES);
    dragonSlayer.changeStrategy(LambdaStrategy.Strategy.ProjectileStrategy);
    dragonSlayer.goToBattle();
    LOGGER.info(BLACK_DRAGON_LANDS);
    dragonSlayer.changeStrategy(LambdaStrategy.Strategy.SpellStrategy);
    dragonSlayer.goToBattle();
  }
}

策略模式主要是分离算法,减少代码的耦合度,而且这个模式很好遵循了开闭原则。在书写代码中,多多使用设计模式,可以让代码更加优美。

二:模板方法模式

2.1 引言

模板方法模式是结构最简单的行为型设计模型,在其结构中只存在父类与之类之间的继承关系,通过使用模板方法模式,可以将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来覆盖某些步骤,从而使得相同的算法框架可以有不同的执行结果。模板方法提供了一个模板方法来定义算法框架,而某些具体步骤的实现可以在其子类中完成。

2.2 定义

模板方法模式:定义一个操作中算法的框架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些步骤。模板方法模式是一种类行为型模式。模板方法定义算法的框架。算法子类为空白部分提供实现。

2.3 优缺点

该模式的主要优点:

  • 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
  • 它在父类中提取了公共的部分代码,便于代码复用。
  • 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

该模式的主要缺点:

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
  • 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。

2.4 适用场景:

  • 对一些复杂算法进行分割,将其算法中固定不变的部分设计为模板方法和父类方法,而一些改变的细节由子类实现,也就是一次性实现算法中不变部分,并将可变部分交由子类实现
  • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复
  • 需要通过子类决定父类算法中某个步骤是否执行,实现子类对父类的反向控制

2.5 直接上demo:

在此示例中, HalflingThief(蒙面小偷) 包含可以更改的 StealingMethod(窃取方法)。 小偷首先使用 HitAndRunMethod(打了就跑) 进行攻击,然后使用 SubtleMethod(微妙方法)。

image.png

StealingMethod 定义算法的框架:

@Slf4j
public abstract class StealingMethod {

  protected abstract String pickTarget();

  protected abstract void confuseTarget(String target);

  protected abstract void stealTheItem(String target);

  /**
   * Steal.
   */
  public final void steal() {
    var target = pickTarget();
    LOGGER.info("The target has been chosen as {}.", target);
    confuseTarget(target);
    stealTheItem(target);
  }
}

蒙面小偷:

public class HalflingThief {

  private StealingMethod method;

  public HalflingThief(StealingMethod method) {
    this.method = method;
  }

  public void steal() {
    method.steal();
  }

  public void changeMethod(StealingMethod method) {
    this.method = method;
  }
}

打了就跑方法:

@Slf4j
public class HitAndRunMethod extends StealingMethod {

  @Override
  protected String pickTarget() {
    return "old goblin woman";
  }

  @Override
  protected void confuseTarget(String target) {
    LOGGER.info("Approach the {} from behind.", target);
  }

  @Override
  protected void stealTheItem(String target) {
    LOGGER.info("Grab the handbag and run away fast!");
  }
}

微妙的方法:

@Slf4j
public class SubtleMethod extends StealingMethod {

  @Override
  protected String pickTarget() {
    return "shop keeper";
  }

  @Override
  protected void confuseTarget(String target) {
    LOGGER.info("Approach the {} with tears running and hug him!", target);
  }

  @Override
  protected void stealTheItem(String target) {
    LOGGER.info("While in close contact grab the {}'s wallet.", target);
  }
}

程序入口:

public class App {

 
  public static void main(String[] args) {
    var thief = new HalflingThief(new HitAndRunMethod());
    thief.steal();
    thief.changeMethod(new SubtleMethod());
    thief.steal();
  }
}