模板方法模式 Template
1、什么是模版方法模式
模版方法模式定义了一个算法的骨架,它将其中一些步骤的实现推迟到子类里面,使得子类可以在不改变算法结构的情况下重新定义算法中的某些步骤。
2、为什么使用模版方法模式
- 封装不变部分:模版方法模式将算法的不变部分封装在父类中,使得子类只需要实现变化的部分,提高了代码的复用性。
- 扩展性:子类可以通过重写父类的方法来扩展或修改算法的行为,提高了灵活性。
- 避免代码重复:将相同的代码放在父类中,避免了在每个子类中重复相同的代码。
3、如何使用模版方法模式
设计实现一个制作咖啡的场景,其中一些步骤是相同的,而一些不同的步骤可以由子类重写
csharp
// 模板类
abstract class CoffeeTemplate {
// 模板方法,定义咖啡的制作步骤
final void makeCoffee() {
boilWater();
brewCoffeeGrounds();
pourInCup();
addCondiments();
}
// 具体步骤,煮沸水
void boilWater() {
System.out.println("Boiling water");
}
// 具体步骤,冲泡咖啡
void brewCoffeeGrounds() {
System.out.println("Brewing coffee grounds");
}
// 具体步骤,倒入杯中
void pourInCup() {
System.out.println("Pouring into cup");
}
// 具体步骤,添加调料,子类实现
abstract void addCondiments();
}
// 具体子类,制作茶
class TeaTemplate extends CoffeeTemplate {
@Override
void addCondiments() {
System.out.println("Adding lemon");
}
}
// 具体子类,制作咖啡
class CoffeeTemplateImpl extends CoffeeTemplate {
@Override
void addCondiments() {
System.out.println("Adding sugar and milk");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
CoffeeTemplate tea = new TeaTemplate();
tea.makeCoffee();
CoffeeTemplate coffee = new CoffeeTemplateImpl();
coffee.makeCoffee();
}
}
4、是否存在缺陷和不足
- 限制子类:模版方法模式可能限制了子类的灵活性,因为在这个模式中要求子类必须遵循父类定义的算法骨架。
- 难以维护:如果模版方法变得复杂,可能会导致难以维护和理解。
5、如何缓解缺陷和不足
- 使用钩子方法:在模版方法中引入钩子方法,允许子类选择性地实现或者覆盖某些步骤,提高灵活性。
csharp
// 模板类
abstract class CoffeeTemplate {
// 模板方法,定义咖啡的制作步骤
final void makeCoffee() {
boilWater();
brewCoffeeGrounds();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
// 具体步骤,煮沸水
void boilWater() {
System.out.println("Boiling water");
}
// 具体步骤,冲泡咖啡
void brewCoffeeGrounds() {
System.out.println("Brewing coffee grounds");
}
// 具体步骤,倒入杯中
void pourInCup() {
System.out.println("Pouring into cup");
}
// 具体步骤,添加调料,子类实现
abstract void addCondiments();
// 钩子方法,决定是否添加调料,默认添加
boolean customerWantsCondiments() {
return true;
}
}
// 具体子类,制作茶
class TeaTemplate extends CoffeeTemplate {
@Override
void addCondiments() {
System.out.println("Adding lemon");
}
// 通过重写钩子方法,决定是否添加调料
@Override
boolean customerWantsCondiments() {
// 用户不想要调料
return false;
}
}
// 具体子类,制作咖啡
class CoffeeTemplateImpl extends CoffeeTemplate {
@Override
void addCondiments() {
System.out.println("Adding sugar and milk");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
CoffeeTemplate tea = new TeaTemplate();
tea.makeCoffee();
CoffeeTemplate coffee = new CoffeeTemplateImpl();
coffee.makeCoffee();
}
}
// 使用钩子方法 customerWantsCondiments 来判断是否执行添加调料的步骤,然后在 TeaTemplate 中
// 重写钩子方法,用户可以选择是否添加。CoffeeTemplateImpl 中没有重写钩子方法,就默认执行
- 使用策略模式:考虑将某些步骤设计成策略对象,允许在运行时切换算法的实现。
csharp
// 策略接口,调料的添加策略
interface CondimentsStrategy {
void addCondiments();
}
// 具体的调料策略,添加柠檬
class LemonCondiments implements CondimentsStrategy {
@Override
public void addCondiments() {
System.out.println("Adding lemon");
}
}
// 具体的调料策略,添加糖和牛奶
class SugarAndMilkCondiments implements CondimentsStrategy {
@Override
public void addCondiments() {
System.out.println("Adding sugar and milk");
}
}
// 模板类
abstract class CoffeeTemplate {
// 策略对象,用于调料的添加
private CondimentsStrategy condimentsStrategy;
// 设置调料策略
void setCondimentsStrategy(CondimentsStrategy condimentsStrategy) {
this.condimentsStrategy = condimentsStrategy;
}
// 模板方法,定义咖啡的制作步骤
final void makeCoffee() {
boilWater();
brewCoffeeGrounds();
pourInCup();
addCondiments();
}
// 具体步骤,煮沸水
void boilWater() {
System.out.println("Boiling water");
}
// 具体步骤,冲泡咖啡
void brewCoffeeGrounds() {
System.out.println("Brewing coffee grounds");
}
// 具体步骤,倒入杯中
void pourInCup() {
System.out.println("Pouring into cup");
}
// 具体步骤,添加调料,通过策略对象调用
void addCondiments() {
if (condimentsStrategy != null) {
condimentsStrategy.addCondiments();
}
}
}
// 具体子类,制作茶
class TeaTemplate extends CoffeeTemplate {
// 构造方法中设置具体的调料策略
TeaTemplate() {
setCondimentsStrategy(new LemonCondiments());
}
}
// 具体子类,制作咖啡
class CoffeeTemplateImpl extends CoffeeTemplate {
// 构造方法中设置具体的调料策略
CoffeeTemplateImpl() {
setCondimentsStrategy(new SugarAndMilkCondiments());
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
CoffeeTemplate tea = new TeaTemplate();
tea.makeCoffee();
CoffeeTemplate coffee = new CoffeeTemplateImpl();
coffee.makeCoffee();
}
}
// 引入 CondimentsStrategy 策略接口和具体的策略实现类:LemonCondiments 和 SugarAndMilkCondiments
// 如此将调料的添加变成一个可变的部分。在 CoffeeTemplateImpl 中引入策略对象,通过
// setCondimentsStrategy 设置具体的调料策略
- 分解大方法:如果模版方法变得复杂,考虑将其分解成多个小方法,使得每个方法都相对简单。
scss
// 模板类
abstract class CoffeeTemplate {
// 策略对象,用于调料的添加
private CondimentsStrategy condimentsStrategy;
// 设置调料策略
void setCondimentsStrategy(CondimentsStrategy condimentsStrategy) {
this.condimentsStrategy = condimentsStrategy;
}
// 模板方法,定义咖啡的制作步骤
final void makeCoffee() {
boilWater();
brewCoffeeGrounds();
pourInCup();
addCondiments();
}
// 具体步骤,煮沸水
void boilWater() {
System.out.println("Boiling water");
}
// 具体步骤,冲泡咖啡
void brewCoffeeGrounds() {
System.out.println("Brewing coffee grounds");
}
// 具体步骤,倒入杯中
void pourInCup() {
System.out.println("Pouring into cup");
}
// 具体步骤,添加调料,通过策略对象调用
void addCondiments() {
if (condimentsStrategy != null) {
condimentsStrategy.addCondiments();
}
}
}
// 具体子类,制作茶
class TeaTemplate extends CoffeeTemplate {
// 构造方法中设置具体的调料策略
TeaTemplate() {
setCondimentsStrategy(new LemonCondiments());
}
// 具体步骤,添加茶叶
void addTeaLeaves() {
System.out.println("Adding tea leaves");
}
}
// 具体子类,制作咖啡
class CoffeeTemplateImpl extends CoffeeTemplate {
// 构造方法中设置具体的调料策略
CoffeeTemplateImpl() {
setCondimentsStrategy(new SugarAndMilkCondiments());
}
// 具体步骤,添加咖啡粉
void addCoffeePowder() {
System.out.println("Adding coffee powder");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
TeaTemplate tea = new TeaTemplate();
tea.makeCoffee();
tea.addTeaLeaves();
CoffeeTemplateImpl coffee = new CoffeeTemplateImpl();
coffee.makeCoffee();
coffee.addCoffeePowder();
}
}
迭代器模式 Iterator
1、什么是迭代器模式
迭代器模式定义了一种方法来顺序访问一个容器对象中的各个元素,而不需要暴露该对象的内部细节,把对元素的访问和遍历从容器对象中分离出来,使得容器和迭代器可以独立地变化。
2、为什么使用迭代器模式
- 分离集合与遍历:迭代器模式将集合对象的遍历行为封装到迭代器中,使得集合与遍历的关注点分离。
- 简化集合接口:迭代器提供了一个统一的遍历接口,简化了集合类的接口,使得集合的实现更加简洁。
- 支持多种遍历方法:通过不同的迭代器实现,可以支持不同的遍历方法,比如正序、逆序、过滤等。
3、如何使用迭代器模式
设计实现一个集合类来实现迭代器模式
java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
// 迭代器接口
interface MyIterator {
boolean hasNext();
Object next();
}
// 集合接口
interface MyCollection {
MyIterator createIterator();
}
// 具体迭代器实现
class MyListIterator implements MyIterator {
private List<Object> list;
private int index = 0;
MyListIterator(List<Object> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return index < list.size();
}
@Override
public Object next() {
if (hasNext()) {
return list.get(index++);
}
return null;
}
}
// 具体集合实现
class MyListCollection implements MyCollection {
private List<Object> list = new ArrayList<>();
// 添加元素
void addElement(Object element) {
list.add(element);
}
// 创建迭代器
@Override
public MyIterator createIterator() {
return new MyListIterator(list);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
MyListCollection collection = new MyListCollection();
collection.addElement("Element 1");
collection.addElement("Element 2");
collection.addElement("Element 3");
// 使用迭代器遍历集合
MyIterator iterator = collection.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
4、是否存在缺陷和不足
迭代器模式不适用于所有的集合,主要是集合内部结构不方便直接使用迭代器的场景。
5、如何缓解缺陷和不足
- 使用增强的 for 循环:对于一些简单的集合,可以使用增强的 for 循环替代迭代器,代码更加简洁。
- 考虑其他遍历方式:如果迭代器不适用于某些集合,可以考虑其他遍历方式,比如通过索引访问元素。