当前位置: 首页 > news >正文

设计模式-行为型模式(详解)

模板方法

模板方法模式,它在一个抽象类中定义了一个算法(业务逻辑)的骨架,具体步骤的实现由子类提供,它通过将算法的不变部分放在抽象类中,可变部分放在子类中,达到代码复用和扩展的目的。

  • 复用: 所有子类可以直接复用父类提供的模板方法,即上面提到的不变的部分。
  • 扩展: 子类可以通过模板定义的一些扩展点就行不同的定制化实现。

我们来看一下示例代码就很清晰了

abstract class AbstractClass {   //模板类// 模板方法public final void templateMethod() {primitiveOperation1();primitiveOperation2();hook();}// 基本操作(抽象方法)protected abstract void primitiveOperation1();protected abstract void primitiveOperation2();// 钩子方法(可选的操作,提供默认实现)protected void hook() {}
}
class ConcreteClassA extends AbstractClass {  //具体方法A@Overrideprotected void primitiveOperation1() {System.out.println("ConcreteClassA: primitiveOperation1");}@Overrideprotected void primitiveOperation2() {System.out.println("ConcreteClassA: primitiveOperation2");}
}
class ConcreteClassB extends AbstractClass {  //具体方法B@Overrideprotected void primitiveOperation1() {System.out.println("ConcreteClassB: primitiveOperation1");}@Overrideprotected void primitiveOperation2() {System.out.println("ConcreteClassB: primitiveOperation2");}@Overrideprotected void hook() {System.out.println("ConcreteClassB: hook");}
}

场景

应用场景:
例如支付场景,需要有支付宝、银行卡、微信支付多种方式,可以定义支付的骨架,骨架里包括一些获取订单、验证密码、执行支付、发送短信的逻辑,像验证密码这些公共的就由骨架来提供,执行支付就由子类来独立实现。

除此之外,支付场景,可能也会涉及到工厂模式,不同的支付方式,利用工厂模式不同的支付对象,再调用支付对象实现不同的功能。

策略模式

策略模式是一种行为型设计模式,提供一个策略接口,并独立实现各种具体策略,允许在运行时动态选择具体策略,从而实现更加灵活的代码结构。该模式的中心不是如何实现算法,而是如何组织、调用这些算法,方便调用方在针对不同场景灵活切换不同的策略。

// 策略的定义
public interface Strategy {void execute(User user); 
}
//具体策略A
public class AStrategy implements Strategy{void execute(User user) {System.out.println("Executing Strategy A " + user.getName());}
}
//具体策略B
public class BStrategy implements Strategy{void execute(User user) {System.out.println("Executing Strategy B " + user.getName());}
}// 策略集合的创建
public class StrategyFactory {private static final Map<String, Strategy> strategies = new HashMap<>();static {strategies.put("A", new AStrategy());strategies.put("B", new BStrategy());}public static Strategy getStrategy(OrderType type) {return strategies.get(type);} 
}// 策略的使用
public class xxService {public void execute(User user) {String type = user.getType();Strategy strategy = StrategyFactory.getStrategy(type);return strategy.execute(user);}
}

可以看到,上述的代码通过使用策略模式,可以将不同的算法策略封装起来,并根据 user 的类型在运行时动态选择具体的算法策略,从而提高系统的灵活性和可扩展性。

可以用在例如多种支付方式的切换、不同排序算法的切换等多策略实现的场景。

责任链模式

责任链模式允许将多个对象连接成一条链,并且沿着这条链传递请求,让多个对象都有机会处理这个请求,请求会顺着链传递,直到某个对象处理它为止。

在很多场景都能看到责任链模式,比如日志的处理,不同级别不同输出。再比如 Spring 过滤器的 Chain 也是责任链模式。

下面是一个简单的日志处理示例代码。

  • 日志处理器有三种类型: 控制台日志处理器(ConsoleLogger)、文件日志处理器(FileLogger)、错误日志处理器(ErrorLogger)。
  • 日志处理器按照优先级进行处理,如果当前处理器不能处理,则将请求传递给下一个处理器。
// 责任链模式的抽象日志类
abstract class Logger {public static int INFO = 1;public static int DEBUG = 2;public static int ERROR = 3;protected int level;protected Logger nextLogger;   //日志中可以存下一个日志public void setNextLogger(Logger nextLogger) {this.nextLogger = nextLogger;}public void logMessage(int level, String message) {if (this.level <= level) {write(message);}if (nextLogger != null) {nextLogger.logMessage(level, message);}}protected abstract void write(String message);
}// 具体处理器类:控制台日志处理器
class ConsoleLogger extends Logger {public ConsoleLogger(int level) {this.level = level;}@Overrideprotected void write(String message) {System.out.println("Standard Console::Logger: " + message);}
}
// 具体处理器类:文件日志处理器
class FileLogger extends Logger {public FileLogger(int level) {this.level = level;}@Overrideprotected void write(String message) {System.out.println("File::Logger: " + message);}
}
// 具体处理器类:错误日志处理器
class ErrorLogger extends Logger {public ErrorLogger(int level) {this.level = level;}@Overrideprotected void write(String message) {System.out.println("Error Console::Logger: " + message);}
}// 客户端代码
public class ChainPatternDemo {private static Logger getChainOfLoggers() {  //获取日志责任链Logger errorLogger = new ErrorLogger(Logger.ERROR);Logger fileLogger = new FileLogger(Logger.DEBUG);Logger consoleLogger = new ConsoleLogger(Logger.INFO);errorLogger.setNextLogger(fileLogger);  //存下一个日志fileLogger.setNextLogger(consoleLogger);   //存再下一个日志return errorLogger;  //最后返回}public static void main(String[] args) {Logger loggerChain = getChainOfLoggers();loggerChain.logMessage(Logger.INFO, "mianshiya.com");loggerChain.logMessage(Logger.DEBUG, "小程序:面试鸭");loggerChain.logMessage(Logger.ERROR, "网页端:mianshiya.com");}
}

观察者模式

观察者模式其实也称为发布订阅模式,它定义了对象之间的一种一对多的依赖关系,让多个观察者对象 同时监听某一个主题对象。当主题对象状态发生变化时,它会通知所有观察者对象。

发布订阅与观察者模式的区别**:发布订阅引入了第三方组件来维护发布者和订阅者的关系,他们之间不直接通信,做到了真正解耦。**

观察者模式的组成部分

  • Subject(主题/被观察者): 状态发生变化时,通知所有注册的观察者
  • Observer(观察者): 接收来自主题的更新通知,并进行相应的操作。
  • ConcreteSubject(具体主题): 实现具体的主题对象,保存需要被观察的状态。
  • ConcreteObserver(具体观察者): 实现具体的观察者对象,更新自己以与主题的状态同步

我们以一个新闻发布系统来理解下观察者模式。

1)定义接口

​ 新闻发布者(Subject)可以发布新闻,观察者(Observer)是订阅者,希望获取新闻更新

2)实现具体类

  • 具体新闻发布者(Concrete Subject)维护一个订阅者列表,并在发布新闻时通知他们
  • 具体观察者(Concrete Observer)如报纸、新闻网站等,实现更新方法来展示新闻

3)订阅和退订

​ 订阅者可以订阅(注册)或退订(注销)新闻发布者的新闻。

4)发布新闻

​ 当新闻发布者有新新闻时,"观察者更新自己的新闻内容。它通知所有订阅的观察者

5)客户端代码

​ 客户端代码创建新闻发布者和观察者对象,订阅者选择订新闻,并在接收到新闻时更新显示。

// 定义观察者接口
interface Observer {void update(String news);
}
// 定义主题接口
interface Subject {void attach(Observer o);void detach(Observer o);void notifyObservers();
}
// 具体新闻发布者
class NewsPublisher implements Subject {private List<Observer> observers = new ArrayList<>();public void attach(Observer o) {observers.add(o);}public void detach(Observer o) {observers.remove(o);}public void notifyObservers() {for (Observer observer : observers) {observer.update("新闻更新");}}public void publishNews() {// 假设这里是新闻发布逻辑,会通知发布者里面集合的观察者notifyObservers();}
}// 具体观察者
class NewsPaper implements Observer {public void update(String news) {System.out.println("新报 " + news);}
}// 客户端代码
public class Client {public static void main(String[] args) {NewsPublisher publisher = new NewsPublisher();  //发布者Observer newsPaper = new NewsPaper();   //观察者publisher.attach(newsPaper);     //导入观察者publisher.publishNews();        //发布新闻publisher.detach(newsPaper);    //移除观察者publisher.publishNews(); // 此时报纸订阅者不会接收到新闻}
}

例如消息队列、 Spring 内的监听器机制等都是其应用场景

迭代器模式

迭代器模式,它提供一种方法顺序访问一个集合对象中的各个元素,而又不暴露该对象(可能是数组、链表、树等等)的内部实现。

将遍历逻辑与集合对象的实现分离,提供一致的遍历方式,使得代码统一化,在不改变遍历代码的情况下就能替换底层集合实现。

像 Java 的java.util.Iterator接口和Iterable接口是迭代器模式的直接应用。所有集合类(如ArrayList、HashSet、LinkedList 等)都实现了 Iterable 接口,并提供了 iterator() 方法来获取迭代器,例如,遍历 ArrayList 中的元素:

List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {System.out.println(iterator.next());
}

再比如 JDBC 的ResultSet接口遍历数据库査询结果,也是迭代器模式的实现。

迭代器模式的组成部分

  • lterator(迭代器接口): 定义访问和历元素的接口
  • Aggregate(聚合接口): 定义创建迭代器的接口
  • Concretelterator(具体迭代器): 实现迭代器接口,负责遍历聚合对象中的元素
  • ConcreteAggregate(具体聚合类): 实现聚合接口,返回一个具体的迭代器实例。

我们来简单实现一个迭代器

//定义迭代器接口
interface Iterator<T> {boolean hasNext();T next();
}
//定义聚合接口
interface Aggregate<T> {Iterator<T> createIterator();
}//定义具体迭代器
class ConcreteIterator<T> implements Iterator<T> {private List<T> items;private int position = 0;  //当前遍历位置public ConcreteIterator(List<T> items) {this.items = items;}@Overridepublic boolean hasNext() {    //如果进行集合遍历时位置<集合大小,则返回truereturn position < items.size();}@Overridepublic T next() {  //获取当前遍历位置的下一个元素return items.get(position++);}
}
//定义具体聚合类
class ConcreteAggregate<T> implements Aggregate<T> {private List<T> items = new ArrayList<>();public void addItem(T item) {   //添加元素方法items.add(item);}@Overridepublic Iterator<T> createIterator() {return new ConcreteIterator<>(items);}
}
//简单使用
public class Client {public static void main(String[] args) {ConcreteAggregate<String> aggregate = new ConcreteAggregate<>();aggregate.addItem("Item 1");    //添加集合元素aggregate.addItem("Item 2");aggregate.addItem("Item 3");Iterator<String> iterator = aggregate.createIterator(); //返回对应该集合的迭代器while (iterator.hasNext()) {System.out.println(iterator.next());}}
}

命令模式

命令模式(Command Pattern)是一种行为设计模式,它将请求封装成对象,使得请求参数化,便于对请求排队或记录请求日志,以及支持可撤销的操作。

简单看下以开关灯为例实现命令模式的代码,更容易理解上面那段含义:

// 命令接口
interface Command {void execute();
}
// 接收者
class Light {   public void on() {    //用于控制灯光的开关System.out.println("The light is on.");}public void off() {System.out.println("The light is off.");}
}// 具体命令   开灯
class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.on();}
}
// 具体命令   关灯
class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.off();}
}
// 调用者,传入了开关命令,因此可以开关灯
class RemoteControl {private Command command;public void setCommand(Command command) {this.command = command;}public void pressButton() {command.execute();}
}// 客户端代码
public class Client {public static void main(String[] args) {Light light = new Light();   Command lightOn = new LightOnCommand(light);  //开灯Command lightOff = new LightOffCommand(light);   //关灯RemoteControl remote = new RemoteControl();remote.setCommand(lightOn);     remote.pressButton();   //开灯remote.setCommand(lightOff);remote.pressButton();    //关灯}
}

状态模式

状态模式 允许对象在其内部状态发生改变时改变其行为,将状态的行为封装在独立的类中,并将这些状态对象组合在拥有状态的对象中,这样就可以在状态改变时切换状态对象,从而改变对象的行为

它主要是状态机的一种实现方式,状态机可分为: 状态、事件、动作三个部分。事件的触发就会导致状态的改变,并且可作出一定的动作(也可以没有动作,只有状态的改变)

// 定义状态接口
interface State {void handle(Context context);
}
// 上下文环境,状态模式的核心,它持有一个 State 对象,并提供了 setState 方法来改变当前状态
class Context {private State state;public Context(State state) {this.state = state;}public void setState(State state) {this.state = state;}public void request() {state.handle(this);}
}
// 具体实现类
//AB每个类在处理请求时都会打印一条消息,并且会改变上下文 Context 的状态为另一种状态
class ConcreteStateA implements State {@Overridepublic void handle(Context context) {System.out.println("State A is handling the request.");context.setState(new ConcreteStateB());}
}
class ConcreteStateB implements State {@Overridepublic void handle(Context context) {System.out.println("State B is handling the request.");context.setState(new ConcreteStateA());}
}public class Client {public static void main(String[] args) {Context context = new Context(new ConcreteStateA());   //新建A状态环境context.request(); // State A is handling the request.   //请求A状态,然后设置当前状态为Bcontext.request(); // State B is handling the request.context.request(); // State A is handling the request.context.request(); // State B is handling the request.}
}

中介者模式

中介模式通过引入了一个中介对象,来封装一组对象之间的交互,来避免对象之间的直接交互。通过引入一个中介者对象,使对象之间的关系变得简单且易于维护。

听起来不就是和现实生活中的中介一样嘛。

引入中介模式的原因:多对象交互可能会使得关系图很混乱,代码也不清晰,让多对象都和中介交互就能避免这点

image-20240928194712824

聊天室实现其实就是运用了中介模式。信息的传递都由服务器这个中介来做,所有用户都把消息发给服务器,不然如果点对点传输大家可以想象下有多复杂。我们可以简单的看下聊天室的实现:

interface ChatMediator {       //中介者接口void sendMessage(String message, User user);void addUser(User user);
}
class ChatMediatorImpl implements ChatMediator { //具体中介者private List<User> users;   //存储加入的用户public ChatMediatorImpl() {this.users = new ArrayList<>();}@Overridepublic void addUser(User user) {this.users.add(user);}@Overridepublic void sendMessage(String message, User user) {for (User u : this.users) {if (u != user) {u.receive(message);}}}
}abstract class User {       //抽象用户protected ChatMediator mediator;protected String name;public User(ChatMediator mediator, String name) {this.mediator = mediator;this.name = name;}public abstract void send(String message);public abstract void receive(String message);
}class UserImpl extends User {    //用户public UserImpl(ChatMediator mediator, String name) {super(mediator, name);}@Overridepublic void send(String message) {System.out.println(this.name + " sends: " + message);mediator.sendMessage(message, this);}@Overridepublic void receive(String message) {System.out.println(this.name + " receives: " + message);}
}
public class ChatClient {    //客户端public static void main(String[] args) {ChatMediator mediator = new ChatMediatorImpl();User user1 = new UserImpl(mediator, "Alice");User user2 = new UserImpl(mediator, "Bob");User user3 = new UserImpl(mediator, "Charlie");mediator.addUser(user1);   //用户加入中介mediator.addUser(user2);mediator.addUser(user3);user1.send("Hello, everyone!");}
}

访问者模式

访问者模式它将数据结构与操作分离,使得你可以在不改变数据结构的前提下定义新的操作。访问者模式通过将操作封装到独立的访问者对象中,使得新的操作可以很容易地添加到系统中。

例如对象序列化场景,比如需要将对象转成 JSON 或者 XML 等格式,利用访问者模式将对象的结构和序列化操作分离,这样就能很方便的扩展新的序列化格式。先看下示例代码:

//访问者接口(类比序列化接口)
interface Visitor {void visit(ElementA element);void visit(ElementB element);
}
//具体访问者类(类比具体序列化实现)
class ConcreteVisitor implements Visitor {@Overridepublic void visit(ElementA element) {System.out.println("Processing ElementA: " + element.getName());}@Overridepublic void visit(ElementB element) {System.out.println("Processing ElementB: " + element.getName());}
}//需要对序列化的元素接口
interface Element {void accept(Visitor visitor);
}// 具体被序列化的元素A
class ElementA implements Element {private String name;public ElementA(String name) {this.name = name;}public String getName() {return name;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}
// 具体被序列化的元素B
class ElementB implements Element {private String name;public ElementB(String name) {this.name = name;}public String getName() {return name;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}//对象结构,包含很多元素
class ObjectStructure {private List<Element> elements = new ArrayList<>();public void addElement(Element element) {elements.add(element);}public void accept(Visitor visitor) {for (Element element : elements) {element.accept(visitor);}}
}// 客户端代码
public class Client {public static void main(String[] args) {// 组装对象ObjectStructure objectStructure = new ObjectStructure();objectStructure.addElement(new ElementA("Element A1"));objectStructure.addElement(new ElementB("Element B1"));objectStructure.addElement(new ElementA("Element A2"));//访问者 (如果是序列化场景,ConcreteVisitor 可以当做 JSON 序列化)Visitor visitor = new ConcreteVisitor();objectStructure.accept(visitor);// 假设后面要替换 xml 序列化,仅需新建 xml 的访问者,然后传入到对象内部即可Visitor visitorXML = new XMLVisitor();objectStructure.accept(visitorXML);}
}

可以看到,将数据结构和操作分离开之后,如果要替换具体的操作,仅需新增一个操作即可,不需要修改任何数据结构,这就符合开闭原则,也保证了类的职责单一。

备忘录模式

备忘录模式指的是在不违背封装原则的前提下,捕获对象内部的状态,将其保存在外部,便于后面对象恢复之前的状态,使得系统更具灵活性和可维护性。

听来像不像保留个快照作为备份,后面基于这个快照进行恢复? 所以备忘录模式其实也叫快照模式。主要用于撤销、恢复等场景。

来看下示例代码,我先大致解释一下几个类的含义:

  • Memento,存储状态的备忘录
  • Originator,需要备份状态的对象类
  • Caretaker,管理者,仅保存备忘录
// 备忘录类
class Memento {private String state;public Memento(String state) {this.state = state;}public String getState() {return state;}
}// 需要备份状态的对象类
class Originator {private String state;public void setState(String state) {this.state = state;System.out.println("State set to: " + state);}public String getState() {return state;}public Memento createMemento() {return new Memento(state);}public void restoreMemento(Memento memento) {this.state = memento.getState();System.out.println("State restored to: " + state);}
}
//管理存储类
class Caretaker {private Memento memento;public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento = memento;}
}//客户端类
public class Client {public static void main(String[] args) {Originator originator = new Originator();  //对象类Caretaker caretaker = new Caretaker();       //存储类originator.setState("State1");caretaker.setMemento(originator.createMemento()); //保存快照originator.setState("State2");System.out.println("Current State: " + originator.getState());originator.restoreMemento(caretaker.getMemento());    //恢复快照System.out.println("Restored State: " + originator.getState());}
}

如果直接利用 set 方法修改内部状态,就违反了封装的原则,因为如果你暴露了 set 方法,则对象内部的状态很可能被别的业务调用修改了。而 restoreMemento 是一个很清晰的方法定义,即恢复之前的状态,不会被乱用。这也是备忘录模式的前提,不违反封装原则。

相关文章:

  • 数据结构 -- 插入排序(直接插入排序和希尔排序)
  • 短剧系统开发与抖音生态融合:短视频时代的新风口与商业机遇
  • Vue组件化与生命周期:打造灵活高效的前端积木世界
  • 深入解析MySQL中的HAVING关键字:从入门到实战
  • vue2组件对象传参
  • Web攻防-SQL注入数据库类型用户权限架构分层符号干扰利用过程发现思路
  • 每天分钟级别时间维度在数据仓库的作用与实现——以Doris和Hive为例(开箱即用)
  • OverLoCK:先概览,再聚焦。CVPR2025全新主干网络
  • 黑马点评--短信登录实现
  • macOS 安装 PostgreSQL
  • 基于BoxMOT的目标检测与跟踪全流程详解
  • HTA8127内置升压的77W单体声D类音频功放
  • 如何在 Windows 11 或 10 上通过 PowerShell 安装 Docker Desktop
  • 大腾智能 PDM 系统:全生命周期管理重塑制造企业数字化转型路径
  • 使用pip安装ptflops报错
  • Spring用到的设计模式
  • Day125 | 灵神 | 二叉树 | 二叉树中的第K大层和
  • 基于RT-Thread的STM32F4开发第七讲——RTC(硬件、软件)
  • C++构造函数和析构函数
  • 【2025最新】下载安装Anaconda
  • 湖南微信网站公司电话/seo 网站优化推广排名教程
  • 做自己网站彩票/网站流量统计分析的维度包括
  • 网站建站哪个品牌好/百度服务
  • 做网站开发学什么语言/长春网络优化哪个公司在做
  • 网站横幅js代码/怎么开个人网站
  • 做五金有哪些网站推广/手机网站免费客服系统