java设计模式之监听者(二)
监听者模式是一种非常经典的设计模式,与访问者模式类型,都是在处理一组复杂数据结构时,将业务逻辑与数据结构解耦。
举一个简单 例子,我们要处理一个Excel对象,Excel包含很多行,每一行有很多列,这就是一个典型的树状结构。
如果我们需要在进入Excel前后、处理每一行前后、甚至处理每一列前后,做一些自定义的动作。
这个时候就应该使用监听者模式,首先定义一个Listener接口。
public interface Listener { void enterExcel(Excel excel); void enterRow(Row row); void exitExcel(Excel excel); void exitRow(Row row); }
Listener接口给外界暴露了可以执行动作的切点,实现Listener接口的对象,可以在给定的这些点做一些需要的动作。
比如下面这个实现类,就在进入和退出Excel时大约Excel的名字,进入每一行之前打印每一行的名称。
public class BaseListener implements Listener { @Override public void enterExcel(Excel excel) { System.out.println("enterExcel="+excel.getName()); } @Override public void exitExcel(Excel excel) { System.out.println("exitExcel="+excel.getName()); } @Override public void enterRow(Row row) { System.out.println("enterRow="+row.getName()); } @Override public void exitRow(Row row) { } }
接下来看我们Excel本身的数据结构。
首先我们定义一个抽象的接口TreeNode,用来规范所有节点的动作。Excel、Row都实现了TreeNode类。
public interface TreeNode { int getChildCount(); TreeNode getChild(int i); void enterNode(Listener listener); void exitNode(Listener listener); } @Data @NoArgsConstructor @AllArgsConstructor public class Row implements TreeNode { private String name; @Override public int getChildCount() { return 0; } @Override public TreeNode getChild(int i) { return null; } @Override public void enterNode(Listener listener) { listener.enterRow(this); } @Override public void exitNode(Listener listener) { listener.exitRow(this); } } @Data public class Excel implements TreeNode { private String name; private List<Row> rowList; public Excel(List<Row> rowList) { this.rowList = rowList; this.name = "CSV"; } @Override public int getChildCount() { return rowList.size(); } @Override public TreeNode getChild(int i) { return rowList.get(i); } @Override public void enterNode(Listener listener) { listener.enterExcel(this); } @Override public void exitNode(Listener listener) { listener.exitExcel(this); } }
监听者模式不同于访问者模式最大的区别在于,监听者模式不负责显示调用子节点的访问方法。
而访问者模式必须显式触发子节点访问以便对树的访问能够正常进行。
访问者模式因为显示调用子节点访问,所有它能够控制访问顺序。
监听者模式就必须自己实现一个遍历逻辑。就像下面的ExcelWalker
public class ExcelWalker { void walk(Listener listener, TreeNode node) { node.enterNode(listener); int childCount = node.getChildCount(); for (int i = 0; i < childCount; i++) { this.walk(listener, node.getChild(i)); } node.exitNode(listener); } }
下面就可以进行实战了。
构造一个Excel对象,它包含5行数据,构造一个我们自己的BaseListener,在创建一个ExcelWalker类
通过ExcelWalker类的walk方法对Excel数据结构进行变量,并传入我们的BaseListener。
public class App { public static void main(String[] args) { List<Row> rows = new ArrayList<>(); for (int i = 0; i < 5; i++) { rows.add(new Row("row-" + i)); } Excel excel = new Excel(rows); BaseListener listener = new BaseListener(); ExcelWalker walker = new ExcelWalker(); walker.walk(listener, excel); } }
输出:
enterExcel=CSV enterRow=row-0 enterRow=row-1 enterRow=row-2 enterRow=row-3 enterRow=row-4 exitExcel=CSV
可以看到,在进入退出Excel都打印了Excel的名称,进入每一行之前都打印了每一行的名称。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
2017-08-25 Python处理图片