作者:明明如月学长, CSDN 博客专家,蚂蚁集团高级 Java 工程师,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《EffectiveJava》独家解析》专栏作者。
热门文章推荐:
- (1)《人工智能时代,软件工程师们将会被取代?》
- (2)《超全人工智能 AI工具导航网站合集》
- (3)《如何写出高质量的文章:从战略到战术》
- (4)《我的技术学习方法论》
- (5)《什么? 你还没用过 Cursor? 智能 AI 代码生成工具 Cursor 安装和使用介绍》
- (6)《我的性能方法论》
- (7)《AI 时代的学习方式: 和文档对话》
- (8)《人工智能终端来了,你还在用过时的 iterm?》
- (9)《无需魔法打开即用的 AI 工具集锦》
一、背景
最近需要调研如何将 SQL 中的表名解析出来。
通过对 New Bing 不断提问,得知 com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser
可以实现这个功能。
继续对 New Bing 进行提问,让 New Bing 帮我们写出 Demo 代码:
能不能使用Java 语言,借助 com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser 将下面的 SQL 中 表名 user 提取出来, SQL : select * form user where userId =10086。
New Bing 的回复:可以使用 Java 语言,借助 com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser
将 SQL 中的表名 user 提取出来。这是一个可能的代码示例:
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.druid.sql.visitor.SchemaStatVisitor;public class Main {public static void main(String[] args) {String sql = "select * from user where userId = 10086";MySqlStatementParser parser = new MySqlStatementParser(sql);SQLStatement statement = parser.parseStatement();SchemaStatVisitor visitor = new SchemaStatVisitor();statement.accept(visitor);System.out.println(visitor.getTables().keySet()); // 输出 [user]}
}
从这份代码中我们发现, druid 中使用了访问者模来实现这部分功能。
于是,我们趁机学习一下访问者模式吧。
二、访问者模式
2.1 使用场景
访问者模式是一种行为型设计模式,它可以让你在不修改对象结构的情况下,为对象结构中的元素添加新的操作。
访问者模式的应用场景是:
- 当你有一个复杂的对象结构(例如一个对象树),并且想在不改变这个结构的前提下,对其中的元素执行不同的操作,而且这些操作可能随着时间的推移而发生变化。
- 当你想将一些与数据结构无关的行为封装到一个类中,而不是分散在多个类中,从而提高代码的可维护性和复用性。
一个典型的例子是 Java 的 ASM 库,它可以用来操作字节码。ASM 提供了一个抽象的访问者接口,让用户可以自定义访问者类来实现对字节码的各种操作,例如修改、增加、删除、优化等。这样,ASM 就可以保持其核心功能的稳定性,同时提供了灵活的扩展性。
2.2 代码示例
有两种类型的游戏:收费游戏和免费游戏,它们都是被访问者。
有两种类型的玩家:付费玩家和免费玩家,它们都是访问者。
不同类型的玩家对不同类型的游戏有不同的操作,例如付费玩家可以下载收费游戏,而免费玩家只能试玩收费游戏。这些操作都是通过访问者模式来实现的,代码如下:
// Game 父类 ( 被访问者 )
public abstract class Game {// 游戏名称private String name;// 游戏价格private double price;public Game(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public double getPrice() {return price;}// 接受访问者的访问public abstract void accept(IVisitor visitor);
}// VipGame 收费游戏 ( 被访问者 )
public class VipGame extends Game {public VipGame(String name, double price) {super(name, price);}@Overridepublic void accept(IVisitor visitor) {// 调用访问者的访问方法visitor.visit(this);}
}// FreeGame 免费游戏 ( 被访问者 )
public class FreeGame extends Game {public FreeGame(String name, double price) {super(name, price);}@Overridepublic void accept(IVisitor visitor) {// 调用访问者的访问方法visitor.visit(this);}
}// IVisitor 访问者接口
public interface IVisitor {// 访问收费游戏void visit(VipGame vipGame);// 访问免费游戏void visit(FreeGame freeGame);
}// VipVisitor 付费玩家 ( 访问者 )
public class VipVisitor implements IVisitor {@Overridepublic void visit(VipGame vipGame) {System.out.println("付费玩家可以下载收费游戏:" + vipGame.getName() + ",价格为:" + vipGame.getPrice());}@Overridepublic void visit(FreeGame freeGame) {System.out.println("付费玩家可以下载免费游戏:" + freeGame.getName() + ",价格为:" + freeGame.getPrice());}
}// FreeVisitor 免费玩家 ( 访问者 )
public class FreeVisitor implements IVisitor {@Overridepublic void visit(VipGame vipGame) {System.out.println("免费玩家只能试玩收费游戏:" + vipGame.getName() + ",价格为:" + vipGame.getPrice());}@Overridepublic void visit(FreeGame freeGame) {System.out.println("免费玩家可以下载免费游戏:" + freeGame.getName() + ",价格为:" + freeGame.getPrice());}
}// 测试类
public class Test {public static void main(String[] args) {// 创建两个游戏对象Game vipGame = new VipGame("英雄联盟", 99.9);Game freeGame = new FreeGame("消消乐", 0);// 创建两个玩家对象IVisitor vipVisitor = new VipVisitor();IVisitor freeVisitor = new FreeVisitor();// 付费玩家访问两个游戏对象vipVisitor.visit(vipGame);vipVisitor.visit(freeGame);// 免费玩家访问两个游戏对象freeVisitor.visit(vipGame);freeVisitor.visit(freeGame);}
}
输出结果为:
付费玩家可以下载收费游戏:英雄联盟,价格为:99.9
付费玩家可以下载免费游戏:消消乐,价格为:0.0
免费玩家只能试玩收费游戏:英雄联盟,价格为:99.9
免费玩家可以下载免费游戏:消消乐,价格为:0.0
2.3 优、缺点
访问者模式的优点有:
- 可以将与数据结构无关的操作封装到访问者类中,从而实现了操作和数据结构的分离,提高了代码的可维护性和复用性。
- 可以在不修改数据结构的前提下,为数据结构添加新的操作,提高了系统的扩展性。
- 可以利用双分派技术,实现多态和动态绑定。
访问者模式的缺点有:
- 增加了系统的复杂性,需要定义访问者接口和具体访问者类,以及被访问者的抽象类和具体类。
- 违反了迪米特原则,访问者需要暴露被访问者的细节,从而降低了系统的封装性。
- 增加了耦合,如果数据结构发生变化,需要修改所有的访问者类,降低了系统的稳定性。
四、总结
大家在日常开发中见到某种设计模式的影子时,可以去源码中学习该功能是如何使用该设计模式来实现的。主动重温该设计模式的适用场景,该设计模式的优点和缺点。这样才能够对该设计模式理解更加深刻,才更容易在未来遇到相关场景时想到该设计模式并加以致用。
创作不易,如果本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励,是我创作的最大动力。
欢迎加入我的知识星球,知识星球ID:15165241 一起交流学习。
https://t.zsxq.com/Z3bAiea 申请时标注来自CSDN。
欢迎加入我们的 slack 工作区,在里面可以对ai 和我进行提问。
https://join.slack.com/t/ai-yx51081/shared_invite/zt-1t8cp1lk3-ZMAFutZcN3PCW~8WQDGjPg