设计模式之六—组合模式:构建树形结构的艺术

设计模式之六:组合模式------构建灵活网络拓扑的艺术

一、什么是组合模式?

1.1 核心定义

组合模式(Composite Pattern) 是一种结构型设计模式 ,它允许你将对象组合成树形结构 来表示"部分-整体"的层次结构。组合模式使得客户端可以统一处理单个对象和组合对象,无需关心处理的是单个元素还是整个组合结构。

1.2 设计意图

  • 将对象组合成树形结构,以表示"部分-整体"的层次关系
  • 客户端代码统一性:客户端可以一致地使用组合结构和单个对象
  • 递归组合:组合对象可以包含其他组合对象,形成树形结构

二、模式结构解析

2.1 UML 类图

复制代码
┌─────────────────────────┐
│      Component          │
├─────────────────────────┤
│ + operation()           │
│ + add(Component)        │
│ + remove(Component)     │
│ + getChild(int)         │
└─────────────────────────┘
          △
          │
   ┌──────┴───────┐
   │              │
┌─────────┐  ┌──────────┐
│  Leaf   │  │ Composite│
├─────────┤  ├──────────┤
│         │  │- children│
│         │  │          │
└─────────┘  └──────────┘

2.2 核心角色

角色 说明 职责
Component 抽象组件 定义组合对象和叶子对象的共同接口
Leaf 叶子节点 表示组合中的叶子对象,没有子节点
Composite 组合节点 存储子组件,实现与子组件相关的操作

三、网关拓扑场景的完美契合

在网关设备管理系统中,组合模式天然适合:

  • 主网关 ← Composite:可以包含从网关和STA
  • 从网关 ← Composite:可以包含其他从网关和STA
  • STA设备 ← Leaf:终端设备,不能再包含其他设备

四、完整代码实现:网关拓扑系统

4.1 抽象组件:网络设备接口

java 复制代码
/**
 * 抽象组件:网络设备接口
 * 定义所有网络设备的通用行为
 */
public abstract class NetworkDevice {
    protected String deviceId;
    protected String name;
    protected String type;
    protected String ip;
    protected String mac;
    protected boolean online;
    
    // 构造函数
    public NetworkDevice(String deviceId, String name, String ip, String mac) {
        this.deviceId = deviceId;
        this.name = name;
        this.ip = ip;
        this.mac = mac;
        this.online = true;
    }
    
    // ================= 通用操作(所有设备都有的)=================
    
    /**
     * 显示设备信息(递归显示)
     */
    public abstract void display(int depth);
    
    /**
     * 获取设备总数(递归统计)
     */
    public abstract int getTotalDevices();
    
    /**
     * 获取在线设备数(递归统计)
     */
    public abstract int getOnlineDevices();
    
    /**
     * 查找设备(递归查找)
     */
    public abstract NetworkDevice findDevice(String deviceId);
    
    /**
     * 获取设备基本信息
     */
    public String getBasicInfo() {
        return String.format(
            "设备ID: %s\n名称: %s\n类型: %s\nIP: %s\nMAC: %s\n状态: %s",
            deviceId, name, type, ip, mac, online ? "在线" : "离线"
        );
    }
    
    // ================= 默认实现(叶子节点不需要这些)=================
    
    /**
     * 添加子设备(默认抛出异常)
     */
    public void addDevice(NetworkDevice device) {
        throw new UnsupportedOperationException(name + "不支持添加子设备");
    }
    
    /**
     * 移除子设备(默认抛出异常)
     */
    public void removeDevice(String deviceId) {
        throw new UnsupportedOperationException(name + "不支持移除子设备");
    }
    
    /**
     * 获取子设备列表(默认抛出异常)
     */
    public List<NetworkDevice> getChildren() {
        throw new UnsupportedOperationException(name + "没有子设备");
    }
    
    // ================= 工具方法 =================
    
    /**
     * 获取缩进字符串
     */
    protected String getIndent(int depth) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < depth; i++) {
            sb.append("│   ");
        }
        return sb.toString();
    }
    
    /**
     * 获取状态图标
     */
    protected String getStatusIcon() {
        return online ? "🟢" : "🔴";
    }
    
    // ================= Getter/Setter =================
    public String getDeviceId() { return deviceId; }
    public String getName() { return name; }
    public String getType() { return type; }
    public String getIp() { return ip; }
    public String getMac() { return mac; }
    public boolean isOnline() { return online; }
    public void setOnline(boolean online) { this.online = online; }
    public void setName(String name) { this.name = name; }
}

4.2 叶子节点:STA终端设备

java 复制代码
import java.util.Date;

/**
 * 叶子节点:STA终端设备
 * 不能再挂载其他设备
 */
public class STADevice extends NetworkDevice {
    private String model;          // 设备型号
    private String firmware;       // 固件版本
    private double uploadMB;       // 上传数据量(MB)
    private double downloadMB;     // 下载数据量(MB)
    private Date lastActive;       // 最后活跃时间
    private int signalStrength;    // 信号强度(0-100)
    
    public STADevice(String deviceId, String name, String ip, String mac, 
                     String model, String firmware) {
        super(deviceId, name, ip, mac);
        this.type = "STA终端";
        this.model = model;
        this.firmware = firmware;
        this.uploadMB = 0;
        this.downloadMB = 0;
        this.lastActive = new Date();
        this.signalStrength = 100;
    }
    
    @Override
    public void display(int depth) {
        String indent = getIndent(depth);
        String signalIcon = getSignalIcon();
        
        System.out.println(indent + "└── " + getStatusIcon() + signalIcon + 
                          " " + name + " [" + type + "]");
        System.out.println(indent + "    ├── ID: " + deviceId);
        System.out.println(indent + "    ├── IP: " + ip);
        System.out.println(indent + "    ├── 信号: " + signalStrength + "%");
        System.out.println(indent + "    ├── 型号: " + model);
        System.out.println(indent + "    ├── 固件: " + firmware);
        System.out.println(indent + "    └── 流量: ↑" + 
                          String.format("%.2f", uploadMB) + "MB / ↓" + 
                          String.format("%.2f", downloadMB) + "MB");
    }
    
    @Override
    public int getTotalDevices() {
        return 1; // STA设备只统计自己
    }
    
    @Override
    public int getOnlineDevices() {
        return online ? 1 : 0;
    }
    
    @Override
    public NetworkDevice findDevice(String deviceId) {
        return this.deviceId.equals(deviceId) ? this : null;
    }
    
    // ================= STA特有方法 =================
    
    /**
     * 更新流量统计
     */
    public void updateTraffic(double upload, double download) {
        this.uploadMB += upload;
        this.downloadMB += download;
        this.lastActive = new Date();
    }
    
    /**
     * 设置信号强度
     */
    public void setSignalStrength(int strength) {
        this.signalStrength = Math.max(0, Math.min(100, strength));
    }
    
    /**
     * 获取详细设备信息
     */
    public String getDetailInfo() {
        return getBasicInfo() + String.format(
            "\n型号: %s\n固件: %s\n信号强度: %d%%\n上传流量: %.2f MB\n下载流量: %.2f MB\n最后活跃: %s",
            model, firmware, signalStrength, uploadMB, downloadMB, lastActive
        );
    }
    
    /**
     * 获取信号图标
     */
    private String getSignalIcon() {
        if (signalStrength >= 80) return "📶";
        if (signalStrength >= 50) return "📶";
        if (signalStrength >= 20) return "📶";
        return "📶";
    }
    
    // Getters
    public String getModel() { return model; }
    public String getFirmware() { return firmware; }
    public double getUploadMB() { return uploadMB; }
    public double getDownloadMB() { return downloadMB; }
    public int getSignalStrength() { return signalStrength; }
}

4.3 组合节点:网关抽象类

java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * 组合节点:网关抽象类
 * 可以挂载其他网关和STA设备
 */
public abstract class Gateway extends NetworkDevice {
    protected List<NetworkDevice> children;
    protected int maxConnections;   // 最大连接数
    protected int currentConnections; // 当前连接数
    protected double cpuUsage;      // CPU使用率
    protected double memoryUsage;   // 内存使用率
    
    public Gateway(String deviceId, String name, String ip, String mac, 
                   int maxConnections) {
        super(deviceId, name, ip, mac);
        this.children = new ArrayList<>();
        this.maxConnections = maxConnections;
        this.currentConnections = 0;
        this.cpuUsage = 0.0;
        this.memoryUsage = 0.0;
    }
    
    @Override
    public void display(int depth) {
        String indent = getIndent(depth);
        String gatewayIcon = getGatewayIcon();
        
        // 显示网关信息
        System.out.println(indent + gatewayIcon + " " + name + " [" + type + "]");
        System.out.println(indent + "├── ID: " + deviceId);
        System.out.println(indent + "├── IP: " + ip);
        System.out.println(indent + "├── 连接: " + currentConnections + "/" + maxConnections);
        System.out.println(indent + "├── 资源: CPU " + 
                          String.format("%.1f", cpuUsage) + "%, 内存 " + 
                          String.format("%.1f", memoryUsage) + "%");
        System.out.println(indent + "├── 子设备: " + children.size() + 
                          "个 (在线: " + getOnlineDevices() + ")");
        
        // 递归显示子设备
        if (!children.isEmpty()) {
            System.out.println(indent + "└── 子设备列表:");
            for (int i = 0; i < children.size(); i++) {
                boolean isLast = (i == children.size() - 1);
                String childIndent = indent + (isLast ? "    " : "│   ");
                System.out.print(childIndent);
                children.get(i).display(depth + 1);
            }
        }
    }
    
    @Override
    public int getTotalDevices() {
        int count = 1; // 统计自己
        for (NetworkDevice child : children) {
            count += child.getTotalDevices();
        }
        return count;
    }
    
    @Override
    public int getOnlineDevices() {
        int count = online ? 1 : 0; // 统计自己
        for (NetworkDevice child : children) {
            count += child.getOnlineDevices();
        }
        return count;
    }
    
    @Override
    public NetworkDevice findDevice(String deviceId) {
        // 先检查自己
        if (this.deviceId.equals(deviceId)) {
            return this;
        }
        
        // 递归查找子设备
        for (NetworkDevice child : children) {
            NetworkDevice found = child.findDevice(deviceId);
            if (found != null) {
                return found;
            }
        }
        return null;
    }
    
    // ================= 重写组合节点特有的方法 =================
    
    @Override
    public void addDevice(NetworkDevice device) {
        if (currentConnections >= maxConnections) {
            throw new IllegalStateException(name + "已达到最大连接数限制");
        }
        
        // 检查设备是否已存在
        for (NetworkDevice child : children) {
            if (child.getDeviceId().equals(device.getDeviceId())) {
                throw new IllegalArgumentException("设备ID " + device.getDeviceId() + " 已存在");
            }
        }
        
        children.add(device);
        currentConnections++;
        updateResourceUsage();
    }
    
    @Override
    public void removeDevice(String deviceId) {
        for (int i = 0; i < children.size(); i++) {
            if (children.get(i).getDeviceId().equals(deviceId)) {
                children.remove(i);
                currentConnections--;
                updateResourceUsage();
                return;
            }
        }
        throw new IllegalArgumentException("未找到设备ID: " + deviceId);
    }
    
    @Override
    public List<NetworkDevice> getChildren() {
        return new ArrayList<>(children);
    }
    
    // ================= 网关特有方法 =================
    
    /**
     * 更新资源使用率(根据连接数模拟计算)
     */
    protected void updateResourceUsage() {
        this.cpuUsage = Math.min(100.0, 5.0 + currentConnections * 3.0);
        this.memoryUsage = Math.min(100.0, 10.0 + currentConnections * 2.5);
    }
    
    /**
     * 获取所有STA设备
     */
    public List<STADevice> getAllSTADevices() {
        List<STADevice> staList = new ArrayList<>();
        collectSTADevices(this, staList);
        return staList;
    }
    
    private void collectSTADevices(NetworkDevice device, List<STADevice> result) {
        if (device instanceof STADevice) {
            result.add((STADevice) device);
        } else if (device instanceof Gateway) {
            for (NetworkDevice child : ((Gateway) device).children) {
                collectSTADevices(child, result);
            }
        }
    }
    
    /**
     * 拓扑统计信息
     */
    public String getTopologyStats() {
        int totalDevices = getTotalDevices();
        int onlineDevices = getOnlineDevices();
        int staCount = getAllSTADevices().size();
        int gatewayCount = totalDevices - staCount;
        
        return String.format(
            "=== 拓扑统计 ===\n" +
            "总设备数: %d\n" +
            "在线设备: %d\n" +
            "网关设备: %d\n" +
            "STA设备: %d\n" +
            "连接率: %.1f%%",
            totalDevices, onlineDevices, gatewayCount, staCount,
            (currentConnections * 100.0 / maxConnections)
        );
    }
    
    /**
     * 获取网关图标(由子类实现)
     */
    protected abstract String getGatewayIcon();
}

4.4 具体网关实现

java 复制代码
/**
 * 主网关实现
 */
public class MasterGateway extends Gateway {
    private String areaCode;        // 区域编码
    private String admin;           // 管理员
    private Date deployTime;        // 部署时间
    
    public MasterGateway(String deviceId, String name, String ip, String mac,
                        int maxConnections, String areaCode, String admin) {
        super(deviceId, name, ip, mac, maxConnections);
        this.type = "主网关";
        this.areaCode = areaCode;
        this.admin = admin;
        this.deployTime = new Date();
    }
    
    @Override
    protected String getGatewayIcon() {
        return "🏠";
    }
    
    @Override
    public void display(int depth) {
        System.out.println("\n" + "=".repeat(70));
        System.out.println("主网关拓扑结构 - " + name + " [" + areaCode + "]");
        System.out.println("管理员: " + admin + " | 部署时间: " + deployTime);
        System.out.println("=".repeat(70));
        super.display(0);
        System.out.println("=".repeat(70));
    }
    
    /**
     * 获取所有从网关
     */
    public List<SlaveGateway> getAllSlaveGateways() {
        List<SlaveGateway> slaves = new ArrayList<>();
        collectSlaveGateways(this, slaves);
        return slaves;
    }
    
    private void collectSlaveGateways(NetworkDevice device, List<SlaveGateway> result) {
        if (device instanceof SlaveGateway) {
            result.add((SlaveGateway) device);
        }
        if (device instanceof Gateway) {
            for (NetworkDevice child : ((Gateway) device).getChildren()) {
                collectSlaveGateways(child, result);
            }
        }
    }
    
    /**
     * 获取主网关详细信息
     */
    public String getMasterInfo() {
        return getBasicInfo() + String.format(
            "\n区域编码: %s\n管理员: %s\n最大连接数: %d\n当前连接: %d\n部署时间: %s",
            areaCode, admin, maxConnections, currentConnections, deployTime
        );
    }
}

/**
 * 从网关实现
 */
public class SlaveGateway extends Gateway {
    private String masterId;        // 所属主网关ID
    private int hopCount;           // 跳数(距离主网关的层级)
    
    public SlaveGateway(String deviceId, String name, String ip, String mac,
                       int maxConnections, String masterId, int hopCount) {
        super(deviceId, name, ip, mac, maxConnections);
        this.type = "从网关";
        this.masterId = masterId;
        this.hopCount = hopCount;
    }
    
    @Override
    protected String getGatewayIcon() {
        return "📡";
    }
    
    /**
     * 获取从网关详细信息
     */
    public String getSlaveInfo() {
        return getBasicInfo() + String.format(
            "\n主网关ID: %s\n跳数: %d\n最大连接数: %d\n当前连接: %d",
            masterId, hopCount, maxConnections, currentConnections
        );
    }
    
    public int getHopCount() {
        return hopCount;
    }
    
    public String getMasterId() {
        return masterId;
    }
}

4.5 客户端演示代码

java 复制代码
/**
 * 组合模式客户端演示
 */
public class CompositePatternDemo {
    public static void main(String[] args) {
        System.out.println("🚀 组合模式演示:网关拓扑管理系统\n");
        
        // ========== 1. 创建拓扑结构 ==========
        // 1.1 创建主网关
        MasterGateway master = new MasterGateway(
            "GW-MASTER-001", "总部核心网关", "10.0.1.1", "00:1A:2B:3C:4D:5E",
            200, "AREA-BJ-001", "系统管理员"
        );
        
        // 1.2 创建一级从网关
        SlaveGateway buildingA = new SlaveGateway(
            "GW-SLAVE-A01", "A栋网关", "10.0.2.1", "00:1A:2B:3C:4D:10",
            50, "GW-MASTER-001", 1
        );
        
        SlaveGateway buildingB = new SlaveGateway(
            "GW-SLAVE-B01", "B栋网关", "10.0.3.1", "00:1A:2B:3C:4D:20", 
            30, "GW-MASTER-001", 1
        );
        
        // 1.3 创建二级从网关
        SlaveGateway floor3 = new SlaveGateway(
            "GW-SLAVE-A03", "A栋3层网关", "10.0.2.3", "00:1A:2B:3C:4D:11",
            20, "GW-SLAVE-A01", 2
        );
        
        // 1.4 创建STA设备
        STADevice server1 = new STADevice(
            "STA-SRV-001", "数据库服务器", "10.0.1.10", "00:1B:2C:3D:4E:5F",
            "Dell R740", "v2.5.1"
        );
        
        STADevice pc1 = new STADevice(
            "STA-PC-001", "开发工作站", "10.0.2.101", "00:1B:2C:3D:4E:70",
            "HP Z4", "Win10 v2004"
        );
        
        STADevice camera1 = new STADevice(
            "STA-CAM-001", "监控摄像头", "10.0.2.201", "00:1B:2C:3D:4E:80",
            "HikVision DS-2CD", "v5.6.5"
        );
        
        STADevice printer1 = new STADevice(
            "STA-PRT-001", "网络打印机", "10.0.3.50", "00:1B:2C:3D:4E:90",
            "HP LaserJet", "v1.8.3"
        );
        
        // ========== 2. 构建拓扑关系 ==========
        System.out.println("🔗 构建拓扑关系...");
        
        // 主网关直连设备
        master.addDevice(buildingA);
        master.addDevice(buildingB);
        master.addDevice(server1);
        
        // A栋网关连接设备
        buildingA.addDevice(floor3);
        buildingA.addDevice(pc1);
        buildingA.addDevice(camera1);
        
        // B栋网关连接设备
        buildingB.addDevice(printer1);
        
        // 3层网关连接设备
        STADevice meetingRoom = new STADevice(
            "STA-MR-001", "会议室终端", "10.0.2.31", "00:1B:2C:3D:4E:99",
            "Conference System", "v3.2.1"
        );
        floor3.addDevice(meetingRoom);
        
        // ========== 3. 设置设备状态 ==========
        System.out.println("⚙️ 设置设备状态...");
        printer1.setOnline(false);                 // 打印机离线
        camera1.setSignalStrength(45);             // 摄像头信号中等
        meetingRoom.updateTraffic(125.5, 320.8);   // 会议室终端有流量
        
        // ========== 4. 显示完整拓扑 ==========
        master.display(0);
        
        // ========== 5. 统一接口演示 ==========
        System.out.println("\n🎯 统一接口演示");
        System.out.println("========================================");
        
        // 创建测试设备数组
        NetworkDevice[] testDevices = {
            master,          // 主网关
            buildingA,       // 从网关  
            server1          // STA设备
        };
        
        for (NetworkDevice device : testDevices) {
            System.out.println("\n设备类型: " + device.getType());
            System.out.println("设备名称: " + device.getName());
            System.out.println("在线状态: " + (device.isOnline() ? "🟢 在线" : "🔴 离线"));
            System.out.println("设备总数: " + device.getTotalDevices());
            System.out.println("在线设备: " + device.getOnlineDevices());
            System.out.println("---");
        }
        
        // ========== 6. 拓扑统计 ==========
        System.out.println("\n📊 拓扑统计信息");
        System.out.println("========================================");
        System.out.println(master.getTopologyStats());
        
        // ========== 7. 设备查找演示 ==========
        System.out.println("\n🔍 设备查找演示");
        System.out.println("========================================");
        
        String searchId = "STA-MR-001";
        NetworkDevice found = master.findDevice(searchId);
        if (found != null) {
            System.out.println("找到设备: " + found.getName());
            if (found instanceof STADevice) {
                System.out.println("详细信息:\n" + ((STADevice) found).getDetailInfo());
            }
        }
        
        // ========== 8. 批量操作演示 ==========
        System.out.println("\n🔄 批量操作演示");
        System.out.println("========================================");
        
        // 8.1 获取所有STA设备
        List<STADevice> allSTAs = master.getAllSTADevices();
        System.out.println("所有STA设备 (" + allSTAs.size() + "台):");
        for (STADevice sta : allSTAs) {
            System.out.println("  • " + sta.getName() + 
                             " (信号: " + sta.getSignalStrength() + "%)");
        }
        
        // 8.2 获取所有从网关
        List<SlaveGateway> allSlaves = master.getAllSlaveGateways();
        System.out.println("\n所有从网关 (" + allSlaves.size() + "台):");
        for (SlaveGateway slave : allSlaves) {
            System.out.println("  • " + slave.getName() + 
                             " (跳数: " + slave.getHopCount() + ")");
        }
        
        // ========== 9. 动态拓扑修改 ==========
        System.out.println("\n⚡ 动态拓扑修改演示");
        System.out.println("========================================");
        
        // 9.1 添加新设备
        STADevice newDevice = new STADevice(
            "STA-NEW-001", "新测试设备", "10.0.2.150", "00:1B:2C:3D:4E:88",
            "Test Device", "v1.0.0"
        );
        
        try {
            buildingA.addDevice(newDevice);
            System.out.println("✅ 成功添加设备: " + newDevice.getName());
        } catch (Exception e) {
            System.out.println("❌ 添加失败: " + e.getMessage());
        }
        
        // 9.2 移除设备
        try {
            buildingA.removeDevice("STA-PC-001");
            System.out.println("✅ 成功移除设备: STA-PC-001");
        } catch (Exception e) {
            System.out.println("❌ 移除失败: " + e.getMessage());
        }
        
        // 9.3 显示更新后的拓扑
        System.out.println("\n🔄 更新后的拓扑结构:");
        master.display(0);
        
        // ========== 10. 异常处理演示 ==========
        System.out.println("\n⚠️ 异常处理演示");
        System.out.println("========================================");
        
        try {
            // STA设备尝试添加子设备(应该抛出异常)
            server1.addDevice(newDevice);
        } catch (UnsupportedOperationException e) {
            System.out.println("捕获到预期异常: " + e.getMessage());
        }
        
        System.out.println("\n✨ 组合模式演示完成!");
    }
}

五、组合模式的两种变体

5.1 透明组合模式(本例采用)

java 复制代码
// 优点:客户端完全透明,无需知道是叶子还是组合节点
// 缺点:叶子节点需要实现不需要的方法(抛出异常)
public abstract class Component {
    public abstract void operation();
    public void add(Component c) { 
        throw new UnsupportedOperationException(); 
    }
}

5.2 安全组合模式

java 复制代码
// 优点:接口安全,叶子节点没有多余方法
// 缺点:客户端需要判断节点类型
public abstract class Component {
    public abstract void operation();
}

public class Composite extends Component {
    private List<Component> children;
    public void add(Component c) { children.add(c); } // 只有组合节点有此方法
}

六、组合模式在网关系统中的优势

6.1 设计优势

优势 说明
统一接口 STA、从网关、主网关使用相同接口
简化客户端 客户端无需区分设备类型
灵活扩展 轻松添加新设备类型
递归处理 自动处理多层级拓扑

6.2 实际应用价值

  1. 设备监控:一键获取全网设备状态
  2. 拓扑发现:自动构建网络拓扑图
  3. 批量配置:统一配置所有设备
  4. 故障定位:快速定位故障点
  5. 资源统计:准确统计网络资源

七、与其他设计模式的关系

7.1 与迭代器模式

java 复制代码
// 组合模式 + 迭代器模式 = 强大的遍历能力
public class GatewayIterator implements Iterator<NetworkDevice> {
    private Stack<NetworkDevice> stack = new Stack<>();
    
    public GatewayIterator(NetworkDevice root) {
        stack.push(root);
    }
    
    public boolean hasNext() {
        return !stack.isEmpty();
    }
    
    public NetworkDevice next() {
        NetworkDevice device = stack.pop();
        if (device instanceof Gateway) {
            List<NetworkDevice> children = ((Gateway) device).getChildren();
            for (int i = children.size() - 1; i >= 0; i--) {
                stack.push(children.get(i));
            }
        }
        return device;
    }
}

7.2 与访问者模式

java 复制代码
// 组合模式 + 访问者模式 = 分离操作与结构
public interface DeviceVisitor {
    void visit(STA device);
    void visit(Gateway gateway);
}

public class StatisticsVisitor implements DeviceVisitor {
    private int totalDevices = 0;
    private int onlineDevices = 0;
    
    public void visit(STA device) {
        totalDevices++;
        if (device.isOnline()) onlineDevices++;
    }
    
    public void visit(Gateway gateway) {
        totalDevices++;
        if (gateway.isOnline()) onlineDevices++;
        // 递归访问子设备
        for (NetworkDevice child : gateway.getChildren()) {
            if (child instanceof STA) visit((STA) child);
            else if (child instanceof Gateway) visit((Gateway) child);
        }
    }
}

八、最佳实践和注意事项

8.1 何时使用组合模式

  • ✅ 需要表示对象的"部分-整体"层次结构
  • ✅ 希望客户端忽略组合与单个对象的差异
  • ✅ 系统中有树形结构的需求
  • ✅ 需要对树中所有节点执行统一操作

8.2 注意事项

  1. 合理设计接口:避免接口过于庞大
  2. 性能考虑:深度递归可能影响性能
  3. 内存管理:注意循环引用问题
  4. 异常处理:合理处理叶子节点不支持的操作

8.3 扩展思考

java 复制代码
// 可能的扩展方向:
// 1. 设备分组:支持按功能分组
// 2. 权限控制:不同设备有不同的管理权限
// 3. 事件通知:设备状态变化通知父节点
// 4. 负载均衡:网关自动分配连接

九、总结

组合模式通过将对象组织成树形结构,实现了部分-整体关系的统一处理。在网关拓扑管理系统中:

  1. 简化了复杂结构:多层级网关关系变得清晰
  2. 统一了操作接口:无论操作单个设备还是整个网络,接口一致
  3. 提高了扩展性:新增设备类型无需修改现有代码
  4. 增强了可维护性:拓扑结构的变化对客户端透明

设计模式的价值 不在于模式的复杂,而在于它提供的设计思想。组合模式教会我们:通过统一接口处理复杂层次结构,可以让系统更灵活、更健壮、更易于维护。

正如计算机科学家克里斯托弗·亚历山大所说:"每个模式都是一个三部分规则,它表达了一个特定上下文、一个问题和一个解决方案之间的关系。" 组合模式就是在"部分-整体"层次结构这个上下文中,解决统一处理问题的优雅方案。

相关推荐
LJianK12 小时前
select .. group by
java·数据库·sql
猿小羽2 小时前
[TEST] Spring Boot 快速入门指南 - 1769246843980
java·spring boot·后端
阿蒙Amon2 小时前
C#每日面试题-简述this和base的作用
java·面试·c#
毕设源码-朱学姐2 小时前
【开题答辩全过程】以 基于SSM的航班管理系统的设计与实现为例,包含答辩的问题和答案
java
indexsunny2 小时前
互联网大厂Java求职面试实战:Spring Boot、微服务与Redis缓存技术解析
java·spring boot·redis·微服务·面试·电商·技术栈
橘色的喵2 小时前
嵌入式 C++ 高性能流式架构的设计
数码相机·设计模式
程序员小白条2 小时前
面试 Java 基础八股文十问十答第二十一期
java·开发语言·数据库·面试·职场和发展
fanruitian2 小时前
k8s pv pvc 持久化存储
java·linux·kubernetes
哪里不会点哪里.2 小时前
Spring MVC 四种核心传参形式详解
java·spring·mvc