上一篇地址:持续总结中!2024年面试必问 20 道设计模式面试题(八)-CSDN博客
十七、模板方法模式(Template Method Pattern)是如何工作的?
模板方法模式(Template Method Pattern)是一种行为型设计模式,它在超类中定义了一个算法的框架,同时允许子类在不改变算法结构的前提下,重新定义算法的特定步骤。
模板方法模式的定义:
模板方法模式通过提供一个抽象类(模板类),在其中定义了一系列基本操作的序列,并在某些步骤中使用钩子(hook)方法来指定子类可以扩展或重写的地方。
模板方法模式的组成:
- 抽象模板类(Abstract Template Class):定义了算法的框架,包括基本操作的序列和钩子方法。
- 具体模板类(Concrete Template Class):继承自抽象模板类,实现或重写钩子方法以及一些基本操作。
- 钩子方法(Hook Method):在抽象模板类中定义的可以被子类重写的方法,用于在算法中提供扩展点。
模板方法模式如何工作:
-
定义算法框架:在抽象模板类中定义算法的骨架,包括算法的各个步骤和执行顺序。
-
基本操作:算法框架中包括一些基本操作,这些操作可以是具体实现的,也可以是抽象的,需要子类实现。
-
钩子方法:在算法框架中定义一些可以被子类重写的方法,这些方法通常用于提供额外的行为或配置。
-
子类实现:具体模板类继承自抽象模板类,并根据需要重写钩子方法和实现抽象操作。
-
算法执行:模板方法提供一个方法(通常是
final
的),用于启动算法的执行。这个方法按照定义好的顺序调用基本操作和钩子方法。 -
扩展性:通过重写钩子方法,子类可以在不改变算法结构的前提下,向算法中添加特定的行为。
使用场景:
- 当需要在多个类中复用相同的算法框架,但允许每个类在某些步骤中有不同的行为时。
- 当需要控制子类扩展,同时提供开放-封闭原则的实现时。
示例:
假设我们有一个咖啡制作流程,其中包括烧水、研磨咖啡豆、冲泡咖啡等步骤。不同的咖啡类型可能在某些步骤上有所不同,例如添加调料。
- 抽象模板类:
CoffeeTemplate
,定义了制作咖啡的算法框架,包括boilWater()
、grindCoffeeBeans()
和brewCoffee()
等基本操作,以及一个addCondiments()
钩子方法。 - 具体模板类:
LatteCoffee
和EspressoCoffee
,继承自CoffeeTemplate
,实现或重写钩子方法和基本操作。 - 客户端:使用具体模板类来制作咖啡。
客户端代码可以这样使用模板方法模式:
CoffeeTemplate coffee;
coffee = new LatteCoffee();
coffee.prepareRecipe(); // 制作拿铁咖啡
coffee = new EspressoCoffee();
coffee.prepareRecipe(); // 制作浓缩咖啡
在这个例子中,CoffeeTemplate
是抽象模板类,定义了咖啡制作的算法框架;LatteCoffee
和EspressoCoffee
是具体模板类,提供了不同的调料添加方法。通过模板方法模式,我们可以保持算法结构的一致性,同时允许每个具体类在某些步骤中有不同的行为。这提供了高度的可扩展性和可维护性。
十八、访问者模式(Visitor Pattern)解决了什么问题?
访问者模式(Visitor Pattern)解决了面向对象设计中的两个主要问题:
-
对象结构的扩展性问题:在传统的面向对象设计中,如果要增加新的操作,通常需要修改已有的对象类。这意味着要更改对象类中的代码,违反了开闭原则(对扩展开放,对修改封闭)。访问者模式通过将操作逻辑从对象结构中分离出来,封装到独立的访问者类中,使得在不修改现有对象结构的情况下,可以增加新的操作。
-
对象结构与操作之间的耦合问题:当对象结构和操作逻辑紧密耦合时,任何对操作逻辑的修改都可能影响到对象结构,反之亦然。这会导致代码难以维护和扩展。访问者模式通过引入一个访问者接口,将操作逻辑集中管理,降低了对象结构和操作逻辑之间的耦合度。
访问者模式的主要优点包括:
- 分离操作逻辑:将操作逻辑从对象结构中分离,使得操作可以在不修改对象结构的情况下添加或修改。
- 增强灵活性:通过访问者模式,可以很容易地定义新的操作,而无需了解对象结构的内部实现。
- 单一职责原则:对象结构专注于其自身的职责,而访问者类负责执行操作,这符合单一职责原则。
- 多态性:访问者模式利用了多态性,可以在运行时动态地将不同的操作应用到不同的对象上。
访问者模式的组成部分:
- 访问者接口(Visitor Interface):定义了对所有具体元素类的操作接口。
- 具体访问者(Concrete Visitor):实现访问者接口,提供对各个具体元素类的访问操作。
- 元素接口(Element Interface):声明了一个接受访问者的接口,通常包含一个
accept
方法。 - 具体元素(Concrete Element):实现元素接口,每个具体元素类可以提供一个接受访问者的方法。
- 对象结构(Object Structure):可以是一个复合集合,允许访问者访问其包含的所有元素,同时提供了遍历集合的机制。
使用场景:
- 当对象结构稳定,但需要对结构中的元素执行多种不同的操作时。
- 当需要在不修改现有类的情况下,向对象结构中添加新的操作时。
- 当对象结构中的元素需要动态地与多种不同的操作关联时。
示例:
假设有一个文档编辑系统,文档由多种不同类型的元素组成,例如段落、图片和表格。我们希望在不修改这些元素类的情况下,添加新的文档操作,如“打印”或“复制”。
- 访问者接口:定义了
visit(Paragraph)
、visit(Image)
和visit(Table)
等方法。 - 具体访问者:如
PrintVisitor
和CopyVisitor
,实现了访问者接口,定义了如何打印或复制文档中的每个元素。 - 元素接口:定义了一个
accept
方法,用于接受访问者。 - 具体元素:如
Paragraph
、Image
和Table
类,实现了元素接口,并通过accept
方法接收访问者。 - 对象结构:如
Document
类,包含一个元素的集合,并提供了遍历这些元素的方法。
通过访问者模式,我们可以轻松地添加新的操作,如添加一个ExportVisitor
来导出文档,而不需要修改现有的元素类。这种模式提高了系统的灵活性和可维护性。