设计模式-行为型模式

caixiaozi / 2023-08-15 / 原文

⾏为模式:负责对象间的⾼效沟通和职责传递委派。

PS:博客根据it老齐大话设计模式课程课件进行整理,IT老齐 视频学习网站: https://www.itlaoqi.com

包含的设计模式:  

  策略模式、模板⽅法模式、观察者模式、迭代⼦模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式

责任链模式

  允许将请求沿着处理者链进⾏发送。收到请求后,每个处理者均可对请求进⾏处理,或将其传递给链上的下个处理者

应用场景

  1、当程序需要使⽤不同⽅式处理不同种类请求, ⽽且请求类型和顺序预先未知时, 可以使⽤责任链模式

  2、当必须按顺序执⾏多个处理者时,可以使⽤该模式。

  3、如果所需处理者及其顺序必须在运⾏时进⾏改变,可以使⽤责任链模式。

代码背景

  权限认证

顶层接口 

public interface Handler {
  public void setNext(Handler next);
  public void handle(Map<String,Object> request);
}

抽象处理器

public abstract class AbstractHandler implements Handler {
  private Handler next;
  public void setNext(Handler next) {
    this.next = next;
  }
  public Handler getNext() {
    return next;
  }
}

认证处理器

import java.util.Map;
public class AuthHandler extends AbstractHandler{
  @Override
  public void handle(Map<String,Object> request) {
    if(request.get("username").equals("admin") && request.get("password").equals("admin")){
      System.out.println("⽤户信息认证通过");
      if(this.getNext() != null){
        this.getNext().handle(request);
      }else{
        throw new RuntimeException("请求没有被送达ProceedHandler");
      }
    }else{
      throw new RuntimeException("⽤户名或密码错误");
    }
  }
}

缓存处理器

import java.util.Map;
public class CacheHandler extends AbstractHandler{
  @Override
  public void handle(Map<String,Object> request) {
    System.out.println("已将当前请求转存⾄Redis缓存");
    if(this.getNext() != null){
      this.getNext().handle(request);
    }else{
      throw new RuntimeException("请求没有被送达ProceedHandler");
    }
  }
}

指标收集处理器

import java.util.Map;
public class MetricsHandler extends AbstractHandler{
  @Override
  public void handle(Map<String,Object> request) {
    System.out.println("已获取当前运⾏指标并发送⾄Prometheus");
    if(this.getNext() != null){
      this.getNext().handle(request);
    }else{
      throw new RuntimeException("请求没有被送达ProceedHandler");
    }
  }
}

业务转发处理器

import java.util.Map;
public class ProceedHandler extends AbstractHandler{
  @Override
  public void handle(Map<String,Object> request) {
    System.out.println("请求已被转发给业务系统,完成后续业务处理");
  }
}

调用类

import java.util.HashMap;
import java.util.Map;
public class Client {
  public static void main(String[] args) {
    Handler authHandler = new AuthHandler();
    Handler cacheHandler = new CacheHandler();
    Handler metricsHandler = new MetricsHandler();
    Handler proceedHandler = new ProceedHandler();
    authHandler.setNext(metricsHandler);
    metricsHandler.setNext(cacheHandler);
    cacheHandler.setNext(proceedHandler);
    Map request = new HashMap<>();
    request.put("username", "admin");
    request.put("password", "admin");
    authHandler.handle(request);
  }
}

优缺点

优点:1、可以控制请求处理的顺序

   2、单⼀职责原则。 可对发起操作和执⾏操作的类进⾏解耦

   3、开闭原则。 可以在不更改现有代码的情况下在程序中新增处理者。

缺点:1、不能保证每个请求⼀定被处理。由于⼀个请求没有明确的接收者,所以不能保证它⼀定会被处理,该请求可能⼀直传到链的末端都得不到处理

   2、对⽐较⻓的职责链,请求的处理可能涉及多个处理对象,系统性能将受到⼀定影响

   3、职责链建⽴的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置⽽导致系统出错,如可能会造成循环调⽤

 

命令模式

  可将请求转换为⼀个包含与请求相关的所有信息的独⽴对象。 该转换可根据不同的请求将⽅法参数化、 延迟请求执⾏或将其放⼊队列中, 且能实现可撤销操作

结构图

应用场景

  1、需要通过操作来参数化对象,可使⽤命令模式

  2、将操作放⼊队列中、操作的执⾏或者远程执⾏操作,可使⽤命令模式

代码实现

应用类

public class ApplicationServer {
  private String ip;
  public ApplicationServer(String ip) {
    this.ip = ip;
  }
  public String getIp() {
    return ip;
  }
  public void setIp(String ip) {
    this.ip = ip;
  }
  public void dump(){
    System.out.println("[" + ip +"]mysqldump -uroot -p --all-databases > /backup/mysqldump/all.db ");
  }
  public void syncTime(){
    System.out.println("[" + ip +"]ntpdate 0.asia.pool.ntp.org");
  }
  public void shutdown(){
    System.out.println("[" + ip +"]showdown now");
  }
}

命令接口类

public interface Command {
 /**
 * 执⾏⽅法
 */
 public void execute();
}

备份命令类

import java.util.ArrayList;
import java.util.List;
public class DumpCommand implements Command{
  List<ApplicationServer> instanceList = new ArrayList();
  public DumpCommand(ApplicationServer instance){
    instanceList.add(instance);
  }
  public DumpCommand(List<ApplicationServer> instances){
    this.instanceList = instances;
  }
  @Override
  public void execute() {
    for (ApplicationServer instance : instanceList){
      System.out.println("[" + instance.getIp() +"]正在执⾏备份数据库命令DUMP");
      instance.dump();
    }
    System.out.println("======================");
  }
}

时钟校准命令类

import java.util.ArrayList;
import java.util.List;
public class SyncTimeCommand implements Command{
  List<ApplicationServer> instanceList = new ArrayList();
  public SyncTimeCommand(ApplicationServer instance){
    instanceList.add(instance);
  }
  public SyncTimeCommand(List<ApplicationServer> instances){
    this.instanceList = instances;
  }
  @Override
  public void execute() {
    for (ApplicationServer instance : instanceList){
      System.out.println("[" + instance.getIp() +"]正在执⾏时钟校准SYNCTIME");
      instance.syncTime();
    }
    System.out.println("======================");
  }
}

关机命令类

import java.util.ArrayList;
import java.util.List;
public class ShutdownCommand implements Command{
  List<ApplicationServer> instanceList = new ArrayList();
  public ShutdownCommand(ApplicationServer instance){
    instanceList.add(instance);
  }
  public ShutdownCommand(List<ApplicationServer> instances){
    this.instanceList = instances;
  }
  @Override
  public void execute() {
    for (ApplicationServer instance : instanceList){
      System.out.println("[" + instance.getIp() +"]正在执⾏关机命令SHUTDOWN");
      instance.shutdown();
    }
    System.out.println("======================");
  }
}

服务管理类

import java.util.ArrayList;
import java.util.List;
/*invoker执⾏者*/
public class ManageServer {
  List<Command> commandList = new ArrayList<>();
  public void addCommand(Command command) {
    commandList.add(command);
  }
  public void execute(){
    for(Command command : commandList){
      command.execute();
    }
  }
}

调用类

import java.util.ArrayList;
import java.util.List;
/**
 * Client客户端
 */
public class Client {
  public static void main(String[] args) {
    List<ApplicationServer> instanceList = new ArrayList<>();
    instanceList.add(new ApplicationServer("192.168.31.223"));
    instanceList.add(new ApplicationServer("192.168.31.203"));
    instanceList.add(new ApplicationServer("192.168.31.126"));
    instanceList.add(new ApplicationServer("192.168.31.183"));

    ManageServer shellExecutor = new ManageServer();
    shellExecutor.addCommand(new DumpCommand(instanceList));
    shellExecutor.addCommand(new SyncTimeCommand(instanceList));
    shellExecutor.addCommand(new ShutdownCommand(instanceList));
    shellExecutor.execute();
  }
}

优缺点

优点:1、单⼀职责原则。 可以解耦触发和执⾏操作的类

   2、开闭原则。 可以在不修改已有客户端代码的情况下在程序中创建新的命令

   3、可以将⼀组简单命令组合成⼀个复杂命令

缺点:1、代码可能会变得更加复杂, 因为在发送者和接收者之间增加了⼀个全新的层次

 

观察者模式

  定义⼀种订阅机制, 可在对象事件发⽣时通知多个 “观察” 该对象的其他对象

应用场景

  1、所有的发布订阅模式

  2、构建事件监听机制,⽐如按下按钮触发click事件

组成部分

  Subject被观察的⽬标

  Observer观察者抽象类

  具体的观察者实现类

  Client客户端

Subject⽬标

  Subject内部持有观察者对象集合,在合适的时机触发事件

public class Server {
  private List<Observer> observers = new ArrayList<Observer>();
  private int cpu;
  private int memory;
  public void changeState(int cpu,int memory){
    this.cpu = cpu;
    this.memory = memory;
    System.out.println("CPU:" + cpu + "%,内存占⽤:" + memory + "%");
    notifyAllObservers();
  }
  public int getCpu() {
    return cpu;
  }
  public int getMemory() {
    return memory;
  }
  public void addObserver(Observer observer){
    observers.add(observer); 
  }
  public void notifyAllObservers(){
    for (Observer observer : observers) {
      observer.update();
    }
  } 
}

 Observer抽象观察者

  内部持有Subject对象

public abstract class Observer {
  protected Server subject;
  public abstract void update();
}

CPU监听器-观察者

  Observer内部持有subject⽬标

public class CpuObserver extends Observer{
  public CpuObserver(Server subject){
    this.subject = subject;
    this.subject.addObserver(this);
  }
  @Override
  public void update() {
    if(subject.getCpu() >= 80){
      System.out.println("警报:CPU当前" + subject.getCpu()+ "%即将满载,请速查明原因");
    }
  }
}

 内存监听器-观察者

public class MemoryObserver extends Observer{
  public MemoryObserver(Server subject){
    this.subject = subject;
    this.subject.addObserver(this);
  }
  @Override
  public void update() {
    if(subject.getMemory() >= 80){
      System.out.println("警报:服务器内存已占⽤超过" + subject.getMemory()+ "%,请速查明原因");
    }
  }
}

调用者

import java.util.Random;
public class Client {
  public static void main(String[] args) throws InterruptedException {
    Server subject = new Server();
    new CpuObserver(subject);
    new MemoryObserver(subject);
    while(true) {
      int cpu = new Random().nextInt(100);
      int memory = new Random().nextInt(100);
      subject.changeState(cpu, memory);
      Thread.sleep(5000);
    }
  }
}

优点

  1、开闭原则。 你⽆需修改发布者代码就能引⼊新的订阅者类 (如果是 发布者接⼝则可轻松引⼊发布者类)

  2、可以在运⾏时建⽴对象之间的联系

 

策略模式

策略接口

public interface UserDao {
  public void insert();
  public void update();
  public void delete();
  public void findById();
}

策略实现类

public class JdbcDao implements UserDao {
  @Override
  public void insert() {
    System.out.println("JDBC⽅式实现数据插⼊");
  }
  @Override
  public void update() {
    System.out.println("JDBC⽅式实现数据更新");
  }
  @Override
  public void delete() {
    System.out.println("JDBC⽅式实现数据删除");
  }
  @Override
  public void findById() {
    System.out.println("JDBC⽅式实现数据查询");
  }
}
public class JndiDao implements UserDao {
  @Override
  public void insert() {
    System.out.println("JNDI⽅式实现数据插⼊");
  }
  @Override
  public void update() {
    System.out.println("JNDI⽅式实现数据更新");
  }
  @Override
  public void delete() {
    System.out.println("JNDI⽅式实现数据删除");
  }
  @Override
  public void findById() {
    System.out.println("JNDI⽅式实现数据查询");
  }
}

Contenxt上下⽂类 

  ⽤于使⽤策略,通过策略接⼝完成代码逻辑

public class UserService {
  public UserDao dao = null;
  public UserService(UserDao dao) {
    this.dao = dao;
  }
  public void createUser(){
    System.out.println("正在创建⽤户对象");
    dao.insert();
  }
}

调用者

import java.util.Scanner;
public class Client {
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    String input = scanner.nextLine();
    UserDao dao = null;
    switch (input) {
      case "jdbc":
        dao = new JdbcDao();
        break;
      case "odbc":
        dao = new OdbcDao();
        break;
      case "jndi":
        dao = new JndiDao();
        break;
    }
    UserService userService = new UserService(dao);
    userService.createUser();
  }
}

优缺点

优点

  1、运⾏时切换对象内的算法

  2、可以将算法的实现和使⽤算法的代码隔离开

  3、可以使⽤组合来代替继承

  4、符合开闭原则

缺点

  1、算法极少发⽣改变,那么没有任何理由引⼊新的类和接⼝。 使⽤该模式只会让程序过于复杂

  2、客户端必须知晓策略间的不同——它需要选择合适的策略

  3、匿名函数中实现不同版本的算法,无需借助额外的类和接口保持代码整洁