《设计模式之行为型模式》系列,共包含以下文章:
- 行为型模式(一):模板方法模式、观察者模式
- 行为型模式(二):策略模式、命令模式
- 行为型模式(三):责任链模式、状态模式
- 行为型模式(四):备忘录模式、中介者模式
- 行为型模式(五):解释器模式、访问者模式、依赖注入
😊 如果您觉得这篇文章有用 ✔️ 的话,请给博主一个一键三连 🚀🚀🚀 吧 (点赞 🧡、关注 💛、收藏 💚)!!!您的支持 💖💖💖 将激励 🔥 博主输出更多优质内容!!!
行为型模式(五):解释器模式、访问者模式
- 9.解释器模式(Interpreter)
- 9.1 代码示例
- 9.1.1 定义表达式接口
- 9.1.2 实现具体表达式类
- 9.1.3 客户端
- 10.访问者模式(Visitor)
- 10.1 代码示例
- 10.1.1 元素接口
- 10.1.2 具体元素
- 10.1.3 访问者接口
- 10.1.4 具体访问者
- 10.1.5 对象结构
- 10.1.6 客户端
- 10.1.7 输出
- 11.依赖注入(Dependency Injection)
- 11.1 代码示例
- 11.1.1 构造器注入(Constructor Injection)
- 11.1.2 设值方法注入(Setter Injection)
- 11.1.3 接口注入
- 11.1.3.1 定义注入接口
- 11.1.3.2 实现注入接口
- 11.1.3.3 客户端
- 11.2 总结
9.解释器模式(Interpreter)
解释器模式(Interpreter
)是一种行为设计模式,它主要用于处理语言、表达式或命令的解析和执行。这种模式定义了如何构建一个解释器来解析特定的语句或命令,并执行相应的操作。下面是解释器模式的一些关键点:
- 文法定义:首先需要定义一个文法,这个文法描述了语言或表达式的结构。例如,一个简单的算术表达式文法可能包括加法、减法等操作。
- 表达式接口:定义一个抽象类或接口,所有具体的表达式类都实现这个接口。接口通常包含一个
interpret
方法,用于解释和执行表达式。 - 具体表达式类:实现表达式接口的具体类,每个类负责解析和执行特定类型的表达式。例如,
AddExpression
类负责处理加法操作。 - 上下文:一个上下文对象,用于存储解析过程中需要的信息,如变量值、中间结果等。
- 客户端:客户端代码将表达式组合成一个大的表达式树,然后调用
interpret
方法来解析和执行整个表达式。
9.1 代码示例
假设我们要解析和执行一个简单的算术表达式,如 1 + 2 * 3
。我们可以使用解释器模式来实现:
9.1.1 定义表达式接口
java">public interface Expression {int interpret();
}
9.1.2 实现具体表达式类
java">public class NumberExpression implements Expression {private int number;public NumberExpression(int number) {this.number = number;}@Overridepublic int interpret() {return number;}
}public class AddExpression implements Expression {private Expression left, right;public AddExpression(Expression left, Expression right) {this.left = left;this.right = right;}@Overridepublic int interpret() {return left.interpret() + right.interpret();}
}public class MultiplyExpression implements Expression {private Expression left, right;public MultiplyExpression(Expression left, Expression right) {this.left = left;this.right = right;}@Overridepublic int interpret() {return left.interpret() * right.interpret();}
}
9.1.3 客户端
java">public class Client {public static void main(String[] args) {Expression expression = new AddExpression(new NumberExpression(1),new MultiplyExpression(new NumberExpression(2),new NumberExpression(3)));int result = expression.interpret();System.out.println("结果: " + result); // 输出: 结果: 7}
}
expression
是一个AddExpression
对象。- 调用
AddExpression
的interpret
方法:left.interpret()
调用NumberExpression(1)
的interpret
方法,返回 1。right.interpret()
调用MultiplyExpression(2, 3)
的interpret
方法:left.interpret()
调用NumberExpression(2)
的interpret
方法,返回 2。right.interpret()
调用NumberExpression(3)
的interpret
方法,返回 3。MultiplyExpression
的interpret
方法返回 2 * 3 = 6。
AddExpression
的interpret
方法返回 1 + 6 = 7。
- 调用
通过这种方式,解释器模式可以灵活地解析和执行复杂的表达式,同时保持代码的可扩展性和可维护性。
10.访问者模式(Visitor)
访问者模式(Visitor
)是一种行为设计模式,它允许你在不改变数据结构的情况下,为数据结构中的元素添加新的操作。这种模式特别适用于数据结构相对稳定,但需要在数据结构上定义很多操作的场景。以下是访问者模式的几个关键点:
- 元素(
Element
):定义一个接受访问者的方法accept(Visitor visitor)
,该方法会调用访问者的方法来访问元素。 - 访问者(
Visitor
):定义一系列访问方法,每个方法对应一种元素类型。访问方法通常命名为visit(Element element)
。 - 具体元素(
ConcreteElement
):实现accept
方法,该方法会调用访问者的一个访问方法。 - 具体访问者(
ConcreteVisitor
):实现访问者接口中的访问方法,对具体元素进行操作。 - 对象结构(
ObjectStructure
):可以是集合或其他数据结构,包含元素对象,并提供方法让访问者访问这些元素。
10.1 代码示例
假设你有一个文档编辑器,文档中包含不同类型的对象,如文本段落和图片。你希望在不修改这些对象的情况下,为它们添加新的操作,比如计算字数或生成缩略图。
10.1.1 元素接口
java">interface Element {void accept(Visitor visitor);
}
10.1.2 具体元素
java">class Paragraph implements Element {private String text;public Paragraph(String text) {this.text = text;}public String getText() {return text;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}class Image implements Element {private String url;public Image(String url) {this.url = url;}public String getUrl() {return url;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}
10.1.3 访问者接口
java">interface Visitor {void visit(Paragraph paragraph);void visit(Image image);
}
10.1.4 具体访问者
java">class WordCountVisitor implements Visitor {private int wordCount = 0;@Overridepublic void visit(Paragraph paragraph) {String[] words = paragraph.getText().split("\\s+");wordCount += words.length;}@Overridepublic void visit(Image image) {// 图片不增加字数}public int getWordCount() {return wordCount;}
}class ThumbnailGeneratorVisitor implements Visitor {@Overridepublic void visit(Paragraph paragraph) {// 文本段落不需要生成缩略图}@Overridepublic void visit(Image image) {System.out.println("Generating thumbnail for image: " + image.getUrl());}
}
10.1.5 对象结构
java">class Document {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);}}
}
10.1.6 客户端
java">public class VisitorPatternDemo {public static void main(String[] args) {Document document = new Document();document.addElement(new Paragraph("Hello, world!"));document.addElement(new Image("http://example.com/image.jpg"));WordCountVisitor wordCountVisitor = new WordCountVisitor();document.accept(wordCountVisitor);System.out.println("Total word count: " + wordCountVisitor.getWordCount());ThumbnailGeneratorVisitor thumbnailGeneratorVisitor = new ThumbnailGeneratorVisitor();document.accept(thumbnailGeneratorVisitor);}
}
10.1.7 输出
java">Total word count: 2
Generating thumbnail for image: http://example.com/image.jpg
通过访问者模式,你可以在不修改文档元素的情况下,为它们添加新的操作,如计算字数和生成缩略图。
11.依赖注入(Dependency Injection)
依赖注入(Dependency Injection
)是一种设计模式,用于实现控制反转(Inversion of Control
,IoC
)。它的主要目的是减少代码之间的耦合,提高代码的可测试性和可维护性。通过依赖注入,对象的依赖关系由外部提供,而不是由对象自己创建或查找。
11.1 代码示例
假设有一个 Logger
接口和两个实现类 FileLogger
和 ConsoleLogger
,以及一个需要日志功能的 UserService
类。
11.1.1 构造器注入(Constructor Injection)
通过构造器传递依赖对象。
- 优点:依赖关系清晰,不可变性好。
- 缺点:构造器参数过多时,代码可读性下降。
java">// Logger 接口
interface Logger {void log(String message);
}// FileLogger 实现
class FileLogger implements Logger {@Overridepublic void log(String message) {System.out.println("File: " + message);}
}// ConsoleLogger 实现
class ConsoleLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Console: " + message);}
}// UserService 类,通过构造器注入 Logger
class UserService {private final Logger logger;public UserService(Logger logger) {this.logger = logger;}public void createUser(String name) {logger.log("Creating user: " + name);// 其他创建用户的逻辑}
}// 客户端代码
public class Main {public static void main(String[] args) {Logger logger = new ConsoleLogger();UserService userService = new UserService(logger);userService.createUser("Alice");}
}
11.1.2 设值方法注入(Setter Injection)
通过设值方法(setter
)传递依赖对象。
- 优点:灵活性高,便于修改依赖关系。
- 缺点:依赖关系不那么明显,对象可能处于不完整状态。
java">// Logger 接口
interface Logger {void log(String message);
}// FileLogger 实现
class FileLogger implements Logger {@Overridepublic void log(String message) {System.out.println("File: " + message);}
}// ConsoleLogger 实现
class ConsoleLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Console: " + message);}
}// UserService 类,通过设值方法注入 Logger
class UserService {private Logger logger;public void setLogger(Logger logger) {this.logger = logger;}public void createUser(String name) {logger.log("Creating user: " + name);// 其他创建用户的逻辑}
}// 客户端代码
public class Main {public static void main(String[] args) {UserService userService = new UserService();Logger logger = new ConsoleLogger();userService.setLogger(logger);userService.createUser("Alice");}
}
11.1.3 接口注入
通过接口方法传递依赖对象。
- 优点:灵活性高,适用于复杂的依赖关系。
- 缺点:实现复杂,使用较少。
11.1.3.1 定义注入接口
java">// Logger 接口
interface Logger {void log(String message);
}// FileLogger 实现
class FileLogger implements Logger {@Overridepublic void log(String message) {System.out.println("File: " + message);}
}// ConsoleLogger 实现
class ConsoleLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Console: " + message);}
}// 注入接口
interface LoggerInjector {void injectLogger(Logger logger);
}
11.1.3.2 实现注入接口
UserService
类需要实现 LoggerInjector
接口,并提供一个方法来注入 Logger
对象。
java">// UserService 类,实现 LoggerInjector 接口
class UserService implements LoggerInjector {private Logger logger;@Overridepublic void injectLogger(Logger logger) {this.logger = logger;}public void createUser(String name) {logger.log("Creating user: " + name);// 其他创建用户的逻辑}
}
11.1.3.3 客户端
java">public class Main {public static void main(String[] args) {// 创建 UserService 对象UserService userService = new UserService();// 创建 Logger 对象Logger logger = new ConsoleLogger();// 通过 LoggerInjector 接口注入 Logger 对象((LoggerInjector) userService).injectLogger(logger);// 使用 UserServiceuserService.createUser("Alice");}
}
- 类型转换
userService
是一个UserService
类的实例。UserService
类实现了LoggerInjector
接口,因此userService
也可以被视为LoggerInjector
类型的对象。- 通过
((LoggerInjector) userService)
,我们将userService
强制转换为LoggerInjector
类型。
- 调用注入方法
LoggerInjector
接口定义了一个injectLogger
方法。- 通过类型转换后,我们可以调用
injectLogger
方法,将Logger
对象注入到UserService
中。
11.2 总结
依赖注入是一种强大的设计模式,通过外部提供依赖关系,使得代码更加灵活、可测试和可维护。
- 降低耦合度:对象不再负责创建或查找其依赖,依赖关系由外部提供。
- 提高可测试性:可以通过注入不同的依赖来测试对象的行为。
- 提高可维护性:依赖关系明确,代码更易于理解和维护。
构造器注入、设值方法注入和接口注入是实现依赖注入的三种主要方式,每种方式都有其适用场景和优缺点。