设计模式-行为型模式
⾏为模式:负责对象间的⾼效沟通和职责传递委派。
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、匿名函数中实现不同版本的算法,无需借助额外的类和接口保持代码整洁