当遍历遇见树形结构:深入理解迭代器模式与组合模式 (行为模式)

在软件设计中,我们常常会遇到各种复杂的对象集合。如何优雅地遍历这些集合?又如何处理像树一样具有层次结构的对象?迭代器模式和组合模式正是为解决这些问题而生的两大利器。很多人会混淆它们,但它们其实是职责分明、又能完美协作的好伙伴。

今天,我们就来一起揭开它们的神秘面纱。

第一部分:迭代器模式——集合的“万能遥控器”

想象一下,你有一个万能遥控器,可以遍历你家所有的电器:电视、空调、音响。你不需要知道每个电器内部是如何换台、调温或切歌的,你只需要按“下一个”按钮。迭代器模式就是这个“万能遥控器”。

1.1 核心思想

迭代器模式提供一种方法,使得我们可以顺序访问一个聚合对象(集合)中的各个元素,而又不需要暴露该对象的内部表示。

它的目标很明确:将遍历行为从集合对象中分离出来。这样,无论是数组、链表、二叉树还是哈希表,客户端都可以用同一套方式(next(), hasNext())来遍历,大大降低了耦合度。

1.2 角色构成
  • 迭代器接口:定义访问和遍历元素的方法,如 hasNext(), next()

  • 具体迭代器:实现迭代器接口,负责管理当前遍历的位置。

  • 聚合接口:定义创建迭代器对象的方法,如 createIterator()

  • 具体聚合:实现聚合接口,返回一个与自身对应的具体迭代器实例。

1.3 简单代码示例
// 迭代器接口
public interface Iterator {
    boolean hasNext();
    Object next();
}

// 聚合接口
public interface Menu {
    Iterator createIterator();
}

// 具体聚合 - 早餐菜单(使用数组)
public class BreakfastMenu implements Menu {
    private MenuItem[] items;

    @Override
    public Iterator createIterator() {
        return new BreakfastMenuIterator(items);
    }
}

// 具体迭代器
public class BreakfastMenuIterator implements Iterator {
    private MenuItem[] items;
    private int position = 0;

    public BreakfastMenuIterator(MenuItem[] items) {
        this.items = items;
    }

    @Override
    public boolean hasNext() {
        return position < items.length && items[position] != null;
    }

    @Override
    public Object next() {
        MenuItem item = items[position];
        position++;
        return item;
    }
}

使用迭代器的客户端代码变得异常简洁:

public class Waitress {
    private Menu breakfastMenu;

    public void printMenu() {
        Iterator iterator = breakfastMenu.createIterator();
        while (iterator.hasNext()) {
            MenuItem item = (MenuItem) iterator.next();
            System.out.println(item.getName() + " - $" + item.getPrice());
        }
    }
}

优势:Waitress类完全不知道菜单项是存储在数组还是列表中,它只依赖 Iterator接口。如果 BreakfastMenu的内部数据结构改变了,Waitress的代码无需任何修改。


第二部分:组合模式——统一的“树形结构”处理专家

现在,考虑一个更复杂的场景:你的菜单本身可以包含子菜单(比如“晚餐菜单”下包含“甜品子菜单”)。这就是一个树形结构。组合模式就是用来处理这种“部分-整体”层次结构的专家。

2.1 核心思想

组合模式将对象组合成树形结构以表示“部分-整体”的层次结构。它使得用户对单个对象(叶子节点)和组合对象(树枝节点)的使用具有一致性。

简单来说,它让我们可以用处理文件的方式去处理文件夹——对两者都执行“打开”操作。对文件是打开本身,对文件夹是打开其窗口。

2.2 角色构成
  • 组件:是组合中所有对象(叶子和组合)的通用接口,声明了如 print()等操作。

  • 叶子:表示叶子节点对象,没有子节点。实现组件接口的行为。

  • 组合:表示树枝节点对象,用于存储子部件(这些子部件可以是叶子,也可以是另一个组合),并实现管理子部件的方法(如 add, remove)。

2.3 代码示例
// 组件接口
public abstract class MenuComponent {
    // 组合相关操作(默认抛出异常,叶子节点不支持)
    public void add(MenuComponent menuComponent) {
        throw new UnsupportedOperationException();
    }
    // 遍历相关操作(由组合节点实现)
    public abstract Iterator createIterator();

    // 公共操作
    public String getName() { ... }
    public void print() { ... }
}

// 叶子节点 - 菜单项
public class MenuItem extends MenuComponent {
    private String name;
    private double price;

    @Override
    public String getName() { return name; }

    @Override
    public void print() {
        System.out.println("  " + getName() + " - $" + getPrice());
    }

    @Override
    public Iterator createIterator() {
        // 菜单项是叶子,没有子元素,返回一个空迭代器
        return new NullIterator();
    }
}

// 组合节点 - 菜单(可包含子菜单或菜单项)
public class Menu extends MenuComponent {
    private List<MenuComponent> menuComponents = new ArrayList<>();
    private String name;

    public Menu(String name) { this.name = name; }

    @Override
    public void add(MenuComponent menuComponent) {
        menuComponents.add(menuComponent);
    }

    @Override
    public void print() {
        System.out.println("\n" + getName());
        System.out.println("----------------");
        // 递归打印所有子组件
        for (MenuComponent component : menuComponents) {
            component.print(); // 这里统一调用了组件的print方法,无论是Menu还是MenuItem
        }
    }

    @Override
    public Iterator createIterator() {
        // 关键!返回一个能遍历所有子组件的迭代器
        return new CompositeIterator(menuComponents.iterator());
    }
}

优势:客户端可以忽略组合对象和单个对象的区别,统一地使用树形结构中的所有对象。


第三部分:强强联合——当组合模式需要遍历时

现在,最精彩的部分来了!如果我们想遍历整个菜单树(包括所有菜单和子菜单),该怎么做?直接在客户端写递归代码吗?这会让客户端变得复杂且与组合结构紧耦合。

答案是:让组合模式与迭代器模式结合!

我们为组合结构中的 Menu(组合类)创建一个特殊的迭代器——CompositeIterator

3.1 组合迭代器

这个迭代器的任务是遍历一个组合对象的所有子组件。由于子组件本身可能也是组合对象(子菜单),所以需要用到栈(Stack)或队列(Queue)来实现深度优先或广度优先遍历。

// 一个深度优先的组合迭代器
public class CompositeIterator implements Iterator {
    private Stack<Iterator<MenuComponent>> stack = new Stack<>();

    public CompositeIterator(Iterator<MenuComponent> iterator) {
        stack.push(iterator);
    }

    @Override
    public boolean hasNext() {
        if (stack.empty()) {
            return false;
        }
        Iterator<MenuComponent> iterator = stack.peek();
        if (!iterator.hasNext()) {
            stack.pop();
            return hasNext(); // 递归检查栈中的下一个迭代器
        } else {
            return true;
        }
    }

    @Override
    public MenuComponent next() {
        if (!hasNext()) {
            return null;
        }
        Iterator<MenuComponent> iterator = stack.peek();
        MenuComponent component = iterator.next();
        if (component instanceof Menu) { // 如果当前组件是子菜单,则将其迭代器压入栈中
            stack.push(component.createIterator());
        }
        return component;
    }
}
3.2 最终的威力

现在,我们的女招待代码可以强大到遍历整个复杂的菜单树,而代码却依然简洁:   

public class PowerfulWaitress {
    private MenuComponent allMenus; // 指向菜单树的根节点

    public PowerfulWaitress(MenuComponent allMenus) {
        this.allMenus = allMenus;
    }

    public void printVegetarianMenu() {
        // 1. 从根菜单创建组合迭代器
        Iterator iterator = allMenus.createIterator();
        System.out.println("\n--- VEGETARIAN MENU ---");

        // 2. 遍历整个树形结构
        while (iterator.hasNext()) {
            MenuComponent component = (MenuComponent) iterator.next();
            try {
                // 3. 对每个元素,我们统一对待。如果是素食菜单项,则打印。
                if (component.isVegetarian()) {
                    component.print();
                }
            } catch (UnsupportedOperationException e) {
                // Menu(组合)节点不支持isVegetarian操作,会抛出异常,这里忽略即可。
            }
        }
    }
}

总结与对比

特性

迭代器模式

组合模式

意图

解耦遍历逻辑,提供统一的遍历接口。

统一处理层次结构,让客户一致对待个体和组合。

关注点

行为(如何访问元素)

结构(如何组织元素)

关键比喻

万能遥控器

文件系统(文件/文件夹)

关系

最佳搭档。组合模式构建的复杂结构,正需要迭代器模式来提供优雅的遍历能力。

总而言之,迭代器模式和组合模式并非同一种模式,它们各有专攻。迭代器模式专注于“如何遍历”,而组合模式专注于“如何构建树形结构”。但当它们结合在一起时,就能以极其优雅的方式处理和管理复杂的对象树,这正是设计模式的魅力所在——它们像积木一样,可以组合出更强大、更灵活的解决方案。

演示了为无线无人机电池充电设计的感应电力传输(IPT)系统 Dynamic Wireless Charging for (UAV) using Inductive Coupling 模拟了为无人机(UAV)量身定制的无线电力传输(WPT)系统。该模型演示了直流电到高频交流电的转换,通过磁共振在气隙中无线传输能量,以及整流回直流电用于电池充电。 系统拓扑包括: 输入级:使用IGBT/二极管开关连接到全桥逆变器的直流电压源(12V)。 开关控制:脉冲发生器以85 kHz(周期:1/85000秒)的开关频率运行,这是SAE J2954无线充电标准的标准频率。 耦合级:使用互感线性变压器块来模拟具有特定耦合系数的发射(Tx)接收(Rx)线圈。 补偿:包括串联RLC分支,用于模拟谐振补偿网络(将线圈调谐到谐振频率)。 输出级:桥式整流器(基于二极管),用于将高频交流电转换回直流电,以供负载使用。 仪器:使用示波器块进行全面的电压电流测量,用于分析输入/输出波形效率。 模拟详细信息: 求解器:离散Tustin/向后Euler(通过powergui)。 采样时间:50e-6秒。 4.主要特点 高频逆变:模拟85 kHz下IGBT的开关瞬态。 磁耦合:模拟无人机着陆垫机载接收器之间的松耦合行为。 Power GUI集成:用于专用电力系统离散仿真的设置。 波形分析:预配置的范围,用于查看逆变器输出电压、初级/次级电流整流直流电压。 5.安装使用 确保您已安装MATLABSimulink。 所需工具箱:必须安装Simscape Electrical(以前称为SimPowerSystems)工具箱才能运行sps_lib块。 打开文件并运行模拟。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值