软件工程结构型设计模式

软件工程结构型设计模式

软件工程中,设计模式提供了相关问题的解决方案,使得工程人员可以更加简单方便的复用成功的设计和体系结构。设计模式分为创建型,结构型,和行为型三大类。本文主要讲结构性设计模式。

引言

结构设型设计模式涉及如何组合类和对象以获得更大的结构。结构型设计模式分为适配器(Adapter),桥接(Bridge),组合(Composite),装饰(Decorator),外观(Facade),享元(Flyweight),代理(Proxy)。这里可以按照ABCD FFP。来进行简单顺序记忆。以下进行详细讲述。

适配器设计模式

通俗解释

想象需要给手机充电,但的充电器插头(两脚扁插)和插座(三脚圆孔)不匹配。这时候有两个选择:

  1. 更换的手机充电器(修改自己的代码)
  2. 使用一个转换插头(适配器)

适配器模式就是这个转换插头:它连接的充电器和酒店的插座,让它们能够正常工作,而不需要改变任何一方。

核心思想将一个类的接口转换成客户端期望的另一种接口,让原本接口不兼容的类可以一起工作。

关键特点

  1. 接口转换:让不兼容的接口能够协同工作
  2. 重用现有代码:无需修改现有类就能集成新功能
  3. 解耦客户端和被适配者:客户端通过适配器与被适配者交互
  4. 符合开闭原则:新增适配器而不修改现有代码

适配器模式类型

  1. 类适配器:通过继承实现(多重继承,Java中不可用,但可通过接口变通)
  2. 对象适配器:通过组合实现(推荐,更灵活)
  3. 接口适配器:缺省适配器(提供接口的默认实现)

现实生活例子

  1. 电源适配器:220V转5V给手机充电
  2. 读卡器:SD卡转USB接口
  3. 翻译官:不同语言之间的沟通桥梁
  4. USB转Type-C转换头:新旧设备连接
  5. 老式游戏手柄转USB:让旧设备在新系统上使用

代码示例

示例1:类适配器(通过继承变通实现)

java 复制代码
// 1. 目标接口:客户端期望的接口(Type-C接口)
interface TypeCCharger {
    void chargeWithTypeC();
    int getPower();
}

// 2. 被适配者:现有的Micro USB充电器
class MicroUSBCharger {
    public void chargeWithMicroUSB() {
        System.out.println("使用Micro USB接口充电中...");
    }
    
    public int outputPower() {
        return 10; // 10W功率
    }
}

// 3. 适配器:将Micro USB转换为Type-C
class USBAdapter extends MicroUSBCharger implements TypeCCharger {
    
    @Override
    public void chargeWithTypeC() {
        System.out.println("适配器:将Type-C转换为Micro USB");
        super.chargeWithMicroUSB(); // 调用被适配者的方法
    }
    
    @Override
    public int getPower() {
        // 适配器还可以增强功能
        int originalPower = super.outputPower();
        System.out.println("原始功率: " + originalPower + "W");
        return originalPower;
    }
    
    // 适配器特有的方法
    public void showAdapterInfo() {
        System.out.println("这是一个Micro USB转Type-C适配器");
    }
}

// 4. 客户端:新款手机(只支持Type-C)
class NewPhone {
    private String model;
    
    public NewPhone(String model) {
        this.model = model;
    }
    
    public void charge(TypeCCharger charger) {
        System.out.println(model + " 开始充电...");
        charger.chargeWithTypeC();
        System.out.println("充电功率: " + charger.getPower() + "W");
        System.out.println("充电完成!\n");
    }
}

// 主程序
public class ClassAdapterDemo {
    public static void main(String[] args) {
        System.out.println("=== 适配器模式示例:类适配器(充电器转换) ===\n");
        
        // 创建新款手机(只支持Type-C)
        NewPhone phone = new NewPhone("iPhone 15");
        
        System.out.println("情景1:使用原装Type-C充电器");
        TypeCCharger typeCCharger = new TypeCCharger() {
            @Override
            public void chargeWithTypeC() {
                System.out.println("直接使用Type-C接口充电");
            }
            
            @Override
            public int getPower() {
                return 20; // 20W快充
            }
        };
        phone.charge(typeCCharger);
        
        System.out.println("情景2:只有旧的Micro USB充电器,使用适配器");
        USBAdapter adapter = new USBAdapter();
        adapter.showAdapterInfo();
        phone.charge(adapter); // 通过适配器使用旧充电器
        
        // 显示适配器工作方式
        System.out.println("=== 适配器工作原理 ===");
        System.out.println("手机 -> TypeC接口 -> 适配器 -> Micro USB接口 -> 充电器");
        System.out.println("客户端(手机)只认识TypeC接口,不知道Micro USB的存在");
    }
}

示例2:对象适配器(通过组合实现,推荐)

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

// 1. 目标接口:现代媒体播放器
interface ModernMediaPlayer {
    void play(String audioType, String fileName);
    void stop();
    void pause();
    List<String> getSupportedFormats();
}

// 2. 被适配者:老式播放器(只能播放MP3)
class LegacyMP3Player {
    public void playMP3(String fileName) {
        System.out.println("播放MP3文件: " + fileName);
        System.out.println("🎵 正在播放音乐...");
    }
    
    public void stopMP3() {
        System.out.println("停止播放MP3");
    }
    
    public void pauseMP3() {
        System.out.println("暂停播放MP3");
    }
    
    public String getPlayerInfo() {
        return "老式MP3播放器 (仅支持MP3格式)";
    }
}

// 3. 另一个被适配者:老式视频播放器
class LegacyVideoPlayer {
    public void playAVI(String fileName) {
        System.out.println("播放AVI视频: " + fileName);
        System.out.println("🎬 正在播放视频...");
    }
    
    public void playWMV(String fileName) {
        System.out.println("播放WMV视频: " + fileName);
        System.out.println("📹 正在播放视频...");
    }
    
    public String getInfo() {
        return "老式视频播放器 (支持AVI, WMV格式)";
    }
}

// 4. 对象适配器:组合多个被适配者
class MediaPlayerAdapter implements ModernMediaPlayer {
    private LegacyMP3Player mp3Player;
    private LegacyVideoPlayer videoPlayer;
    private Map<String, Runnable> playCommands;
    
    public MediaPlayerAdapter() {
        this.mp3Player = new LegacyMP3Player();
        this.videoPlayer = new LegacyVideoPlayer();
        initPlayCommands();
    }
    
    private void initPlayCommands() {
        playCommands = new HashMap<>();
        playCommands.put("mp3", () -> mp3Player.playMP3(""));
        playCommands.put("avi", () -> videoPlayer.playAVI(""));
        playCommands.put("wmv", () -> videoPlayer.playWMV(""));
    }
    
    @Override
    public void play(String audioType, String fileName) {
        System.out.println("适配器:尝试播放 " + audioType.toUpperCase() + " 文件: " + fileName);
        
        switch (audioType.toLowerCase()) {
            case "mp3":
                mp3Player.playMP3(fileName);
                break;
            case "avi":
                videoPlayer.playAVI(fileName);
                break;
            case "wmv":
                videoPlayer.playWMV(fileName);
                break;
            default:
                System.out.println("不支持的文件格式: " + audioType);
                System.out.println("尝试使用默认播放器...");
                // 可以在这里添加更多适配逻辑
                if (audioType.endsWith("mp4") || audioType.endsWith("mkv")) {
                    System.out.println("提示:请安装新版播放器以支持 " + audioType + " 格式");
                }
        }
    }
    
    @Override
    public void stop() {
        System.out.println("适配器:停止播放");
        mp3Player.stopMP3();
        // 视频播放器没有stop方法,但适配器提供了统一接口
    }
    
    @Override
    public void pause() {
        System.out.println("适配器:暂停播放");
        mp3Player.pauseMP3();
        // 视频播放器没有pause方法,但适配器提供了统一接口
    }
    
    @Override
    public List<String> getSupportedFormats() {
        return Arrays.asList("mp3", "avi", "wmv");
    }
    
    // 适配器特有的方法
    public void showLegacyPlayersInfo() {
        System.out.println("\n适配器内部使用的播放器:");
        System.out.println("1. " + mp3Player.getPlayerInfo());
        System.out.println("2. " + videoPlayer.getInfo());
    }
    
    // 增强功能:格式转换
    public void convertFormat(String sourceFile, String targetFormat) {
        System.out.println("格式转换: " + sourceFile + " -> " + targetFormat);
        // 模拟格式转换
        if (targetFormat.equals("mp3")) {
            System.out.println("转换为MP3成功!");
        }
    }
}

// 5. 另一个适配器:仅适配MP3播放器
class SimpleMP3Adapter implements ModernMediaPlayer {
    private LegacyMP3Player mp3Player;
    
    public SimpleMP3Adapter(LegacyMP3Player mp3Player) {
        this.mp3Player = mp3Player;
    }
    
    @Override
    public void play(String audioType, String fileName) {
        if (!audioType.equalsIgnoreCase("mp3")) {
            System.out.println("错误:此适配器仅支持MP3格式");
            return;
        }
        mp3Player.playMP3(fileName);
    }
    
    @Override
    public void stop() {
        mp3Player.stopMP3();
    }
    
    @Override
    public void pause() {
        mp3Player.pauseMP3();
    }
    
    @Override
    public List<String> getSupportedFormats() {
        return Arrays.asList("mp3");
    }
}

// 6. 客户端:现代媒体播放应用
class MediaPlayerApp {
    private ModernMediaPlayer player;
    
    public void setPlayer(ModernMediaPlayer player) {
        this.player = player;
        System.out.println("设置播放器: " + player.getClass().getSimpleName());
        System.out.println("支持的格式: " + player.getSupportedFormats());
    }
    
    public void playFile(String fileName) {
        if (player == null) {
            System.out.println("请先设置播放器");
            return;
        }
        
        String extension = getFileExtension(fileName);
        System.out.println("\n播放文件: " + fileName);
        player.play(extension, fileName);
    }
    
    public void playMultipleFiles(List<String> files) {
        System.out.println("\n=== 播放多个文件 ===");
        for (String file : files) {
            playFile(file);
        }
    }
    
    private String getFileExtension(String fileName) {
        int dotIndex = fileName.lastIndexOf('.');
        return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
    }
}

// 主程序
public class ObjectAdapterDemo {
    public static void main(String[] args) {
        System.out.println("=== 适配器模式示例:对象适配器(媒体播放器) ===\n");
        
        // 创建客户端应用
        MediaPlayerApp app = new MediaPlayerApp();
        
        System.out.println("情景1:使用多功能适配器(支持多种格式)");
        MediaPlayerAdapter multiAdapter = new MediaPlayerAdapter();
        app.setPlayer(multiAdapter);
        
        // 播放多种格式文件
        List<String> files = Arrays.asList(
            "song.mp3",
            "movie.avi", 
            "video.wmv",
            "new_video.mp4"  // 不支持格式
        );
        app.playMultipleFiles(files);
        
        // 显示适配器内部信息
        multiAdapter.showLegacyPlayersInfo();
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        System.out.println("情景2:使用简单适配器(仅MP3)");
        LegacyMP3Player oldPlayer = new LegacyMP3Player();
        SimpleMP3Adapter simpleAdapter = new SimpleMP3Adapter(oldPlayer);
        app.setPlayer(simpleAdapter);
        
        app.playFile("music.mp3");
        app.playFile("video.mp4"); // 这个会失败
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        System.out.println("情景3:直接使用现代播放器(不需要适配器)");
        ModernMediaPlayer modernPlayer = new ModernMediaPlayer() {
            @Override
            public void play(String audioType, String fileName) {
                System.out.println("现代播放器直接播放: " + fileName);
            }
            
            @Override
            public void stop() {
                System.out.println("现代播放器停止");
            }
            
            @Override
            public void pause() {
                System.out.println("现代播放器暂停");
            }
            
            @Override
            public List<String> getSupportedFormats() {
                return Arrays.asList("mp3", "mp4", "avi", "mkv", "flac");
            }
        };
        
        app.setPlayer(modernPlayer);
        app.playFile("high_quality.flac");
        
        // 适配器模式的优势展示
        System.out.println("\n" + "=".repeat(50));
        System.out.println("=== 适配器模式的优势 ===");
        System.out.println("1. 重用现有代码: 无需重写LegacyMP3Player和LegacyVideoPlayer");
        System.out.println("2. 接口统一: 所有播放器都有相同的ModernMediaPlayer接口");
        System.out.println("3. 灵活扩展: 可以轻松添加对新格式的支持");
        System.out.println("4. 符合开闭原则: 添加新适配器不需要修改现有代码");
    }
}

示例3:接口适配器(缺省适配器)

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

// 1. 复杂接口:包含很多方法
interface FileOperations {
    void createFile(String path);
    void deleteFile(String path);
    void renameFile(String oldPath, String newPath);
    void copyFile(String source, String destination);
    void moveFile(String source, String destination);
    void readFile(String path);
    void writeFile(String path, String content);
    void appendToFile(String path, String content);
    void setPermissions(String path, String permissions);
    void getFileInfo(String path);
    List<String> listFiles(String directory);
    boolean fileExists(String path);
    long getFileSize(String path);
    Date getLastModified(String path);
}

// 2. 缺省适配器(抽象类):提供所有方法的默认实现(空实现或抛异常)
abstract class FileOperationsAdapter implements FileOperations {
    @Override
    public void createFile(String path) {
        throw new UnsupportedOperationException("createFile not implemented");
    }
    
    @Override
    public void deleteFile(String path) {
        throw new UnsupportedOperationException("deleteFile not implemented");
    }
    
    @Override
    public void renameFile(String oldPath, String newPath) {
        throw new UnsupportedOperationException("renameFile not implemented");
    }
    
    @Override
    public void copyFile(String source, String destination) {
        throw new UnsupportedOperationException("copyFile not implemented");
    }
    
    @Override
    public void moveFile(String source, String destination) {
        throw new UnsupportedOperationException("moveFile not implemented");
    }
    
    @Override
    public void readFile(String path) {
        throw new UnsupportedOperationException("readFile not implemented");
    }
    
    @Override
    public void writeFile(String path, String content) {
        throw new UnsupportedOperationException("writeFile not implemented");
    }
    
    @Override
    public void appendToFile(String path, String content) {
        throw new UnsupportedOperationException("appendToFile not implemented");
    }
    
    @Override
    public void setPermissions(String path, String permissions) {
        throw new UnsupportedOperationException("setPermissions not implemented");
    }
    
    @Override
    public void getFileInfo(String path) {
        throw new UnsupportedOperationException("getFileInfo not implemented");
    }
    
    @Override
    public List<String> listFiles(String directory) {
        throw new UnsupportedOperationException("listFiles not implemented");
    }
    
    @Override
    public boolean fileExists(String path) {
        throw new UnsupportedOperationException("fileExists not implemented");
    }
    
    @Override
    public long getFileSize(String path) {
        throw new UnsupportedOperationException("getFileSize not implemented");
    }
    
    @Override
    public Date getLastModified(String path) {
        throw new UnsupportedOperationException("getLastModified not implemented");
    }
}

// 3. 具体实现:只实现需要的几个方法
class SimpleFileReader extends FileOperationsAdapter {
    @Override
    public void readFile(String path) {
        System.out.println("读取文件: " + path);
        // 实际读取文件的逻辑
    }
    
    @Override
    public boolean fileExists(String path) {
        System.out.println("检查文件是否存在: " + path);
        return true; // 简化实现
    }
    
    @Override
    public void getFileInfo(String path) {
        System.out.println("获取文件信息: " + path);
        System.out.println("大小: 1024 bytes");
        System.out.println("类型: txt");
    }
}

// 4. 另一个具体实现:实现更多方法
class FileManager extends FileOperationsAdapter {
    private Map<String, String> fileSystem = new HashMap<>();
    
    @Override
    public void createFile(String path) {
        System.out.println("创建文件: " + path);
        fileSystem.put(path, "");
    }
    
    @Override
    public void deleteFile(String path) {
        System.out.println("删除文件: " + path);
        fileSystem.remove(path);
    }
    
    @Override
    public void writeFile(String path, String content) {
        System.out.println("写入文件: " + path);
        fileSystem.put(path, content);
    }
    
    @Override
    public void readFile(String path) {
        System.out.println("读取文件: " + path);
        String content = fileSystem.get(path);
        if (content != null) {
            System.out.println("内容: " + content);
        } else {
            System.out.println("文件不存在");
        }
    }
    
    @Override
    public List<String> listFiles(String directory) {
        System.out.println("列出目录: " + directory);
        List<String> files = new ArrayList<>();
        for (String path : fileSystem.keySet()) {
            if (path.startsWith(directory)) {
                files.add(path);
            }
        }
        return files;
    }
    
    @Override
    public boolean fileExists(String path) {
        return fileSystem.containsKey(path);
    }
}

// 5. 客户端
class ClientApp {
    public void useFileOperations(FileOperations fileOps) {
        System.out.println("\n使用文件操作接口:");
        
        try {
            fileOps.createFile("/test.txt");
        } catch (UnsupportedOperationException e) {
            System.out.println("功能不支持: createFile");
        }
        
        try {
            fileOps.readFile("/test.txt");
        } catch (UnsupportedOperationException e) {
            System.out.println("功能不支持: readFile");
        }
        
        try {
            fileOps.deleteFile("/test.txt");
        } catch (UnsupportedOperationException e) {
            System.out.println("功能不支持: deleteFile");
        }
        
        try {
            List<String> files = fileOps.listFiles("/");
            System.out.println("文件列表: " + files);
        } catch (UnsupportedOperationException e) {
            System.out.println("功能不支持: listFiles");
        }
    }
}

// 主程序
public class InterfaceAdapterDemo {
    public static void main(String[] args) {
        System.out.println("=== 适配器模式示例:接口适配器(缺省适配器) ===\n");
        
        ClientApp app = new ClientApp();
        
        System.out.println("情景1:使用简单文件阅读器");
        SimpleFileReader reader = new SimpleFileReader();
        app.useFileOperations(reader);
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        System.out.println("情景2:使用完整文件管理器");
        FileManager manager = new FileManager();
        app.useFileOperations(manager);
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        System.out.println("=== 接口适配器的优势 ===");
        System.out.println("1. 避免实现不需要的方法");
        System.out.println("2. 简化接口实现");
        System.out.println("3. 提供默认实现,子类只需覆盖需要的方法");
        System.out.println("4. 常用于事件监听器等有很多方法的接口");
        
        // 实际应用示例:Java中的WindowAdapter
        System.out.println("\n=== Java中的实际应用 ===");
        System.out.println("java.awt.event.WindowAdapter 就是一个接口适配器");
        System.out.println("它实现了WindowListener接口的所有方法(空实现)");
        System.out.println("用户只需要继承WindowAdapter并覆盖需要的方法");
        
        // 模拟WindowAdapter
        System.out.println("\n模拟代码:");
        System.out.println("// 原本需要实现7个方法");
        System.out.println("class MyWindowListener implements WindowListener {");
        System.out.println("    // 必须实现所有7个方法,即使大部分是空的");
        System.out.println("}");
        
        System.out.println("\n// 使用适配器后");
        System.out.println("class MyWindowListener extends WindowAdapter {");
        System.out.println("    // 只需要覆盖需要的方法");
        System.out.println("    public void windowClosing(WindowEvent e) {");
        System.out.println("        System.exit(0);");
        System.out.println("    }");
        System.out.println("}");
    }
}

示例4:实际应用 - 第三方支付接口适配

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

// 1. 目标接口:公司统一的支付接口
interface CompanyPaymentGateway {
    boolean makePayment(double amount, String currency, String customerId);
    String getPaymentStatus(String transactionId);
    boolean refund(String transactionId, double amount);
    String generateInvoice(double amount, String customerEmail);
}

// 2. 被适配者1:支付宝接口(第三方)
class AlipayService {
    // 支付宝的接口与我们的接口不同
    public boolean alipayPayment(double rmbAmount, String alipayAccount) {
        System.out.println("调用支付宝接口: 向 " + alipayAccount + " 支付 " + rmbAmount + " 元");
        // 实际调用支付宝API
        return Math.random() > 0.1; // 90%成功率
    }
    
    public String checkAlipayOrder(String alipayOrderId) {
        System.out.println("查询支付宝订单: " + alipayOrderId);
        return "SUCCESS";
    }
    
    public boolean alipayRefund(String alipayOrderId, double rmbAmount) {
        System.out.println("支付宝退款: 订单 " + alipayOrderId + " 退款 " + rmbAmount + " 元");
        return true;
    }
    
    public String getAlipayServiceInfo() {
        return "支付宝支付服务";
    }
}

// 3. 被适配者2:PayPal接口(第三方,国外)
class PayPalService {
    // PayPal的接口也不同
    public boolean processPayPalPayment(double usdAmount, String paypalEmail) {
        System.out.println("调用PayPal接口: 向 " + paypalEmail + " 支付 $" + usdAmount);
        // 实际调用PayPal API
        return Math.random() > 0.05; // 95%成功率
    }
    
    public String getPayPalTransactionStatus(String paypalTransactionId) {
        System.out.println("查询PayPal交易状态: " + paypalTransactionId);
        return "COMPLETED";
    }
    
    public boolean issuePayPalRefund(String paypalTransactionId, double usdAmount) {
        System.out.println("PayPal退款: 交易 " + paypalTransactionId + " 退款 $" + usdAmount);
        return true;
    }
    
    public String createPayPalInvoice(double amount, String email) {
        return "PayPal Invoice #" + System.currentTimeMillis() + " for $" + amount + " to " + email;
    }
}

// 4. 被适配者3:银行转账接口
class BankTransferService {
    public boolean bankTransfer(double amount, String currency, String bankAccount, String beneficiary) {
        System.out.println("银行转账: 向 " + beneficiary + " 的账户 " + bankAccount + " 转账 " + amount + " " + currency);
        // 实际调用银行API
        return Math.random() > 0.2; // 80%成功率
    }
    
    public String checkBankTransfer(String referenceNumber) {
        System.out.println("查询银行转账状态: " + referenceNumber);
        return "PROCESSED";
    }
}

// 5. 适配器1:支付宝适配器
class AlipayAdapter implements CompanyPaymentGateway {
    private AlipayService alipayService;
    private SimpleDateFormat dateFormat;
    
    public AlipayAdapter() {
        this.alipayService = new AlipayService();
        this.dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    }
    
    @Override
    public boolean makePayment(double amount, String currency, String customerId) {
        System.out.println("支付宝适配器: 开始处理支付");
        
        // 货币转换(假设业务逻辑)
        double rmbAmount = convertCurrency(amount, currency, "CNY");
        
        // 生成支付宝订单ID
        String alipayOrderId = "ALIPAY" + dateFormat.format(new Date());
        
        // 调用支付宝服务
        boolean success = alipayService.alipayPayment(rmbAmount, customerId);
        
        if (success) {
            System.out.println("支付宝支付成功,订单号: " + alipayOrderId);
        }
        
        return success;
    }
    
    @Override
    public String getPaymentStatus(String transactionId) {
        System.out.println("支付宝适配器: 查询支付状态");
        // 转换交易ID格式(如果需要)
        String alipayOrderId = convertTransactionId(transactionId);
        return alipayService.checkAlipayOrder(alipayOrderId);
    }
    
    @Override
    public boolean refund(String transactionId, double amount) {
        System.out.println("支付宝适配器: 处理退款");
        String alipayOrderId = convertTransactionId(transactionId);
        double rmbAmount = convertCurrency(amount, "USD", "CNY"); // 假设原始是USD
        return alipayService.alipayRefund(alipayOrderId, rmbAmount);
    }
    
    @Override
    public String generateInvoice(double amount, String customerEmail) {
        System.out.println("支付宝适配器: 生成发票");
        return "支付宝电子发票 - 金额: " + amount + " - 客户: " + customerEmail;
    }
    
    private double convertCurrency(double amount, String fromCurrency, String toCurrency) {
        // 简化汇率转换
        if (fromCurrency.equals("USD") && toCurrency.equals("CNY")) {
            return amount * 7.0;
        } else if (fromCurrency.equals("CNY") && toCurrency.equals("USD")) {
            return amount / 7.0;
        }
        return amount; // 其他情况不转换
    }
    
    private String convertTransactionId(String transactionId) {
        // 转换交易ID格式
        if (transactionId.startsWith("COMPANY")) {
            return "ALIPAY" + transactionId.substring(7);
        }
        return transactionId;
    }
}

// 6. 适配器2:PayPal适配器
class PayPalAdapter implements CompanyPaymentGateway {
    private PayPalService paypalService;
    
    public PayPalAdapter() {
        this.paypalService = new PayPalService();
    }
    
    @Override
    public boolean makePayment(double amount, String currency, String customerId) {
        System.out.println("PayPal适配器: 开始处理支付");
        
        // 货币转换
        double usdAmount = convertCurrency(amount, currency, "USD");
        
        // 调用PayPal服务
        boolean success = paypalService.processPayPalPayment(usdAmount, customerId);
        
        if (success) {
            System.out.println("PayPal支付成功");
        }
        
        return success;
    }
    
    @Override
    public String getPaymentStatus(String transactionId) {
        System.out.println("PayPal适配器: 查询支付状态");
        return paypalService.getPayPalTransactionStatus(transactionId);
    }
    
    @Override
    public boolean refund(String transactionId, double amount) {
        System.out.println("PayPal适配器: 处理退款");
        double usdAmount = convertCurrency(amount, "CNY", "USD"); // 假设原始是CNY
        return paypalService.issuePayPalRefund(transactionId, usdAmount);
    }
    
    @Override
    public String generateInvoice(double amount, String customerEmail) {
        System.out.println("PayPal适配器: 生成发票");
        return paypalService.createPayPalInvoice(amount, customerEmail);
    }
    
    private double convertCurrency(double amount, String fromCurrency, String toCurrency) {
        // 简化汇率转换
        if (fromCurrency.equals("CNY") && toCurrency.equals("USD")) {
            return amount / 7.0;
        } else if (fromCurrency.equals("USD") && toCurrency.equals("CNY")) {
            return amount * 7.0;
        }
        return amount;
    }
}

// 7. 适配器工厂
class PaymentAdapterFactory {
    public static CompanyPaymentGateway getPaymentGateway(String gatewayType) {
        switch (gatewayType.toLowerCase()) {
            case "alipay":
                return new AlipayAdapter();
            case "paypal":
                return new PayPalAdapter();
            default:
                throw new IllegalArgumentException("不支持的支付网关: " + gatewayType);
        }
    }
}

// 8. 客户端:电商系统
class ECommerceSystem {
    public void processOrder(double amount, String currency, String customerEmail, 
                            String paymentMethod, String customerId) {
        System.out.println("\n=== 处理订单 ===");
        System.out.println("金额: " + amount + " " + currency);
        System.out.println("客户: " + customerEmail);
        System.out.println("支付方式: " + paymentMethod);
        
        try {
            // 通过工厂获取适配器
            CompanyPaymentGateway gateway = PaymentAdapterFactory.getPaymentGateway(paymentMethod);
            
            // 使用统一的接口进行支付
            boolean paymentSuccess = gateway.makePayment(amount, currency, customerId);
            
            if (paymentSuccess) {
                System.out.println("✅ 支付成功!");
                
                // 生成发票
                String invoice = gateway.generateInvoice(amount, customerEmail);
                System.out.println("发票: " + invoice);
                
                // 发货等后续操作
                shipOrder(customerEmail);
            } else {
                System.out.println("❌ 支付失败,请重试");
            }
            
        } catch (Exception e) {
            System.out.println("支付处理错误: " + e.getMessage());
        }
    }
    
    private void shipOrder(String customerEmail) {
        System.out.println("📦 订单已发货给: " + customerEmail);
    }
    
    public void refundOrder(String transactionId, double amount, String paymentMethod) {
        System.out.println("\n=== 处理退款 ===");
        System.out.println("交易ID: " + transactionId);
        System.out.println("退款金额: " + amount);
        
        try {
            CompanyPaymentGateway gateway = PaymentAdapterFactory.getPaymentGateway(paymentMethod);
            boolean refundSuccess = gateway.refund(transactionId, amount);
            
            if (refundSuccess) {
                System.out.println("✅ 退款成功");
            } else {
                System.out.println("❌ 退款失败");
            }
        } catch (Exception e) {
            System.out.println("退款处理错误: " + e.getMessage());
        }
    }
}

// 主程序
public class RealWorldAdapterDemo {
    public static void main(String[] args) {
        System.out.println("=== 适配器模式示例:实际应用(第三方支付集成) ===\n");
        
        ECommerceSystem system = new ECommerceSystem();
        
        System.out.println("情景1:中国客户使用支付宝");
        system.processOrder(100.0, "CNY", "zhangsan@example.com", "alipay", "zhangsan123");
        
        System.out.println("\n" + "=".repeat(60));
        
        System.out.println("情景2:美国客户使用PayPal");
        system.processOrder(50.0, "USD", "john@example.com", "paypal", "john456");
        
        System.out.println("\n" + "=".repeat(60));
        
        System.out.println("情景3:处理退款");
        system.refundOrder("TXN123456", 30.0, "alipay");
        
        System.out.println("\n" + "=".repeat(60));
        
        System.out.println("=== 适配器模式带来的好处 ===");
        System.out.println("1. 统一接口: 所有支付方式都使用CompanyPaymentGateway接口");
        System.out.println("2. 易于扩展: 添加新支付方式只需新增适配器,不修改现有代码");
        System.out.println("3. 隔离变化: 第三方API变化时,只需修改适配器,不影响业务逻辑");
        System.out.println("4. 代码复用: 多个系统可以使用相同的适配器");
        
        System.out.println("\n=== 系统架构图 ===");
        System.out.println("电商系统 -> CompanyPaymentGateway接口 -> 适配器 -> 第三方支付API");
        System.out.println("电商系统不知道支付宝、PayPal的具体实现,只认识统一接口");
        
        // 演示添加新的支付方式
        System.out.println("\n" + "=".repeat(60));
        System.out.println("演示:添加新的支付方式(微信支付)");
        
        // 假设新增微信支付
        class WeChatPayService {
            public boolean wechatPayment(double amount, String openId) {
                System.out.println("微信支付: 向OpenID " + openId + " 收取 " + amount + " 元");
                return true;
            }
        }
        
        // 创建微信支付适配器
        class WeChatPayAdapter implements CompanyPaymentGateway {
            private WeChatPayService wechatPay = new WeChatPayService();
            
            @Override
            public boolean makePayment(double amount, String currency, String customerId) {
                System.out.println("微信支付适配器: 处理支付");
                double cnyAmount = amount; // 简化,假设已经是CNY
                return wechatPay.wechatPayment(cnyAmount, customerId);
            }
            
            @Override
            public String getPaymentStatus(String transactionId) {
                return "SUCCESS";
            }
            
            @Override
            public boolean refund(String transactionId, double amount) {
                System.out.println("微信支付退款");
                return true;
            }
            
            @Override
            public String generateInvoice(double amount, String customerEmail) {
                return "微信支付发票";
            }
        }
        
        // 扩展工厂
        System.out.println("\n只需:");
        System.out.println("1. 创建WeChatPayAdapter实现CompanyPaymentGateway");
        System.out.println("2. 在PaymentAdapterFactory中添加wechat分支");
        System.out.println("3. 电商系统无需任何修改即可支持微信支付");
    }
}

适配器模式的优缺点

优点:

  1. 提高类的复用性:可以重用现有的类,无需修改
  2. 提高类的透明度:客户端通过目标接口与被适配者交互
  3. 灵活性好:可以随时增加新的适配器
  4. 符合开闭原则:可以在不修改现有代码的情况下引入新适配器

缺点:

  1. 增加复杂性:增加了很多适配器类,使系统变得复杂
  2. 可能降低性能:由于多了一层调用,可能会稍微降低性能
  3. 过度使用问题:如果系统设计合理,可能不需要适配器

使用场景

  1. 系统需要复用现有类:但这些类的接口不符合系统需求
  2. 统一多个类的接口:多个类完成相似功能但接口不同
  3. 旧系统升级:新系统需要与旧系统兼容
  4. 第三方库集成:需要将第三方库集成到自己的系统中
  5. 接口版本兼容:新接口需要向后兼容旧接口

适配器模式 vs 其他模式

模式 区别
适配器 vs 装饰器 适配器改变接口,装饰器增强功能但不改变接口
适配器 vs 外观 适配器转换一个接口,外观简化多个接口
适配器 vs 代理 适配器改变接口,代理控制访问但不改变接口
适配器 vs 桥接 适配器使不兼容接口兼容,桥接分离抽象和实现

Java中的适配器模式

  1. InputStreamReader/OutputStreamWriter:字节流到字符流的适配器
  2. Arrays.asList():数组到List的适配器
  3. Spring的HandlerAdapter:适配不同类型的处理器
  4. Java GUI中的适配器类:WindowAdapter、MouseAdapter等

适配器模式的核心价值在于:让不兼容的接口能够一起工作,它是系统集成和代码复用的重要工具。通过适配器,我们可以将已有的类集成到新系统中,而无需修改它们的源代码,这大大提高了代码的复用性和系统的可扩展性。

桥接设计模式

简单理解

想象有一个遥控器(抽象)和一台电视(实现)。遥控器可以控制电视,但不希望遥控器只能控制特定型号的电视。桥接模式就像在遥控器和电视之间建立一个桥梁,让它们可以独立变化:可以更换不同的遥控器(比如学习型遥控器、手机APP遥控器),也可以更换不同的电视(索尼、三星、小米),而它们之间都能正常工作。

核心思想

将抽象(做什么)与实现(怎么做)分离,使它们可以独立变化,而不互相影响。

实际场景类比

  • 电商系统:支付方式(微信、支付宝、银行卡)和支付类型(即时支付、分期支付)可以独立扩展
  • 绘图程序:形状(圆形、方形)和颜色(红色、蓝色)可以自由组合
  • 汽车制造:发动机类型(汽油、电动)和车型(SUV、轿车)可以分别开发

代码示例

场景:跨平台消息推送

我们要实现一个消息推送系统,支持不同类型的消息(文本、图片)和不同的推送平台(iOS、Android),并且可以灵活组合。

java 复制代码
// 1. 实现部分接口:推送平台
interface PushPlatform {
    void push(String title, String content);
}

// 2. 具体实现:iOS推送平台
class IOSPlatform implements PushPlatform {
    @Override
    public void push(String title, String content) {
        System.out.println("[iOS推送]");
        System.out.println("标题: " + title);
        System.out.println("内容: " + content);
        System.out.println("使用APNs服务推送...");
    }
}

// 3. 具体实现:Android推送平台
class AndroidPlatform implements PushPlatform {
    @Override
    public void push(String title, String content) {
        System.out.println("[Android推送]");
        System.out.println("标题: " + title);
        System.out.println("内容: " + content);
        System.out.println("使用FCM服务推送...");
    }
}

// 4. 抽象部分:消息类型
abstract class Message {
    protected PushPlatform platform;
    
    public Message(PushPlatform platform) {
        this.platform = platform;
    }
    
    public abstract void send(String title, String content);
}

// 5. 扩展抽象:文本消息
class TextMessage extends Message {
    public TextMessage(PushPlatform platform) {
        super(platform);
    }
    
    @Override
    public void send(String title, String content) {
        System.out.println("准备发送文本消息...");
        // 可以在发送前做一些文本消息特有的处理
        String processedContent = "📝 " + content;
        platform.push(title, processedContent);
    }
}

// 6. 扩展抽象:图片消息
class ImageMessage extends Message {
    public ImageMessage(PushPlatform platform) {
        super(platform);
    }
    
    @Override
    public void send(String title, String content) {
        System.out.println("准备发送图片消息...");
        // 可以在发送前做一些图片消息特有的处理
        String processedContent = "🖼️ [图片消息] " + content;
        platform.push(title, processedContent);
    }
}

// 7. 客户端使用
public class BridgePatternDemo {
    public static void main(String[] args) {
        System.out.println("=== 桥接模式示例:消息推送系统 ===\n");
        
        // 创建不同的推送平台
        PushPlatform ios = new IOSPlatform();
        PushPlatform android = new AndroidPlatform();
        
        // 组合:文本消息 + iOS平台
        System.out.println("1. 发送文本消息到iOS设备:");
        Message textToIOS = new TextMessage(ios);
        textToIOS.send("系统通知", "您有新消息,请查收");
        
        System.out.println("\n---\n");
        
        // 组合:文本消息 + Android平台
        System.out.println("2. 发送文本消息到Android设备:");
        Message textToAndroid = new TextMessage(android);
        textToAndroid.send("系统通知", "您有新消息,请查收");
        
        System.out.println("\n---\n");
        
        // 组合:图片消息 + iOS平台
        System.out.println("3. 发送图片消息到iOS设备:");
        Message imageToIOS = new ImageMessage(ios);
        imageToIOS.send("图片分享", "查看好友分享的图片");
        
        System.out.println("\n---\n");
        
        // 组合:图片消息 + Android平台
        System.out.println("4. 发送图片消息到Android设备:");
        Message imageToAndroid = new ImageMessage(android);
        imageToAndroid.send("图片分享", "查看好友分享的图片");
        
        System.out.println("\n=== 添加新功能演示 ===");
        
        // 添加新的推送平台(比如Web平台)非常容易
        PushPlatform webPlatform = new PushPlatform() {
            @Override
            public void push(String title, String content) {
                System.out.println("[Web推送] 使用WebSocket推送: " + title + " - " + content);
            }
        };
        
        // 添加新的消息类型(比如语音消息)也非常容易
        Message voiceMessage = new Message(webPlatform) {
            @Override
            public void send(String title, String content) {
                System.out.println("发送语音消息...");
                platform.push(title, "🎵 " + content);
            }
        };
        
        System.out.println("\n5. 发送语音消息到Web平台:");
        voiceMessage.send("语音消息", "您收到一条语音留言");
    }
}

更简洁的示例:形状和颜色

java 复制代码
// 实现部分:颜色
interface Color {
    String fill();
}

class Red implements Color {
    public String fill() { return "红色"; }
}

class Blue implements Color {
    public String fill() { return "蓝色"; }
}

// 抽象部分:形状
abstract class Shape {
    protected Color color;
    
    public Shape(Color color) {
        this.color = color;
    }
    
    abstract String draw();
}

class Circle extends Shape {
    public Circle(Color color) {
        super(color);
    }
    
    public String draw() {
        return "绘制一个" + color.fill() + "的圆形";
    }
}

class Square extends Shape {
    public Square(Color color) {
        super(color);
    }
    
    public String draw() {
        return "绘制一个" + color.fill() + "的正方形";
    }
}

// 使用
public class SimpleBridgeDemo {
    public static void main(String[] args) {
        Shape redCircle = new Circle(new Red());
        Shape blueSquare = new Square(new Blue());
        
        System.out.println(redCircle.draw());  // 绘制一个红色的圆形
        System.out.println(blueSquare.draw()); // 绘制一个蓝色的正方形
        
        // 添加新颜色或新形状都非常容易
    }
}

桥接模式的优势

  1. 解耦抽象和实现:可以独立扩展消息类型和推送平台
  2. 提高扩展性:添加新平台或新消息类型不需要修改现有代码
  3. 符合开闭原则:对扩展开放,对修改关闭
  4. 减少子类数量 :如果没有桥接模式,可能需要 iOS文本消息Android文本消息iOS图片消息Android图片消息 等多个类

使用场景

  • 当一个类有两个独立变化的维度,且这两个维度都需要扩展时
  • 避免在多个对象间使用继承导致类爆炸
  • 需要在运行时切换不同的实现

桥接模式的关键在于识别出系统中可以独立变化的两个维度,然后通过组合(而不是继承)的方式将它们连接起来。

组合设计模式

通俗解释

想象一下公司组织架构图:公司有多个部门,每个部门有多个小组,每个小组有多名员工。当想计算整个公司的工资总额时,不需要分别计算每个部门、每个小组、每个员工的工资然后相加,而是可以统一对待:无论是公司、部门、小组还是员工,都有统一的"计算工资"方法。这就是组合模式。

核心思想:用树形结构表示"部分-整体"的层次结构,让单个对象(叶子节点)和组合对象(容器节点)能够被一致对待。

现实生活中的例子

  1. 计算机文件系统:文件夹(容器)和文件(叶子)都有"打开"、"删除"、"重命名"等操作
  2. 公司组织架构:部门(容器)和员工(叶子)都有"计算成本"的方法
  3. 菜单系统:菜单(容器)和菜单项(叶子)都有"显示"和"执行"的方法
  4. 图形编辑器:图形组(容器)和基本图形(叶子)都有"绘制"、"移动"等方法

关键特点

  • 统一的接口:所有对象使用同一套接口
  • 透明性:客户端不需要区分处理的是叶子节点还是容器节点
  • 递归结构:容器可以包含其他容器或叶子

代码示例

示例1:文件系统(经典例子)

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

// 1. 组件接口:定义文件和文件夹的共同操作
interface FileSystemComponent {
    void showDetails(String indent);
    long getSize();
    void add(FileSystemComponent component); // 注意:叶子节点也需要实现此方法(可选抛出异常)
    void remove(FileSystemComponent component);
}

// 2. 叶子节点:文件
class File implements FileSystemComponent {
    private String name;
    private long size;
    
    public File(String name, long size) {
        this.name = name;
        this.size = size;
    }
    
    @Override
    public void showDetails(String indent) {
        System.out.println(indent + "📄 " + name + " (" + size + " bytes)");
    }
    
    @Override
    public long getSize() {
        return size;
    }
    
    @Override
    public void add(FileSystemComponent component) {
        // 文件是叶子节点,不能添加子组件
        throw new UnsupportedOperationException("不能向文件添加组件");
    }
    
    @Override
    public void remove(FileSystemComponent component) {
        throw new UnsupportedOperationException("文件没有子组件");
    }
}

// 3. 容器节点:文件夹
class Directory implements FileSystemComponent {
    private String name;
    private List<FileSystemComponent> components = new ArrayList<>();
    
    public Directory(String name) {
        this.name = name;
    }
    
    @Override
    public void showDetails(String indent) {
        System.out.println(indent + "📁 " + name + " [目录]");
        // 递归显示所有子组件
        for (FileSystemComponent component : components) {
            component.showDetails(indent + "    ");
        }
    }
    
    @Override
    public long getSize() {
        long totalSize = 0;
        // 递归计算所有子组件的大小
        for (FileSystemComponent component : components) {
            totalSize += component.getSize();
        }
        return totalSize;
    }
    
    @Override
    public void add(FileSystemComponent component) {
        components.add(component);
    }
    
    @Override
    public void remove(FileSystemComponent component) {
        components.remove(component);
    }
    
    // 可选:添加一些文件夹特有的方法
    public int getComponentCount() {
        return components.size();
    }
}

// 4. 客户端使用
public class CompositePatternDemo {
    public static void main(String[] args) {
        System.out.println("=== 组合模式示例:文件系统 ===\n");
        
        // 创建文件(叶子节点)
        File file1 = new File("简历.pdf", 2048);
        File file2 = new File("报告.docx", 4096);
        File file3 = new File("照片.jpg", 8192);
        File file4 = new File("代码.java", 1024);
        File file5 = new File("笔记.txt", 512);
        
        // 创建文件夹(容器节点)
        Directory documents = new Directory("文档");
        Directory pictures = new Directory("图片");
        Directory work = new Directory("工作");
        Directory root = new Directory("我的电脑");
        
        // 构建树形结构
        documents.add(file1);
        documents.add(file2);
        documents.add(file5);
        
        pictures.add(file3);
        
        work.add(file4);
        work.add(documents); // 文件夹可以包含文件夹
        
        root.add(work);
        root.add(pictures);
        
        // 统一操作:显示所有内容
        System.out.println("文件系统结构:");
        root.showDetails("");
        
        System.out.println("\n--- 统计信息 ---");
        System.out.println("根目录总大小: " + root.getSize() + " bytes");
        System.out.println("文档文件夹大小: " + documents.getSize() + " bytes");
        System.out.println("工作文件夹组件数: " + work.getComponentCount());
        
        // 演示透明性:统一处理文件和文件夹
        System.out.println("\n--- 统一处理示例 ---");
        FileSystemComponent[] components = {file1, documents, file3};
        
        for (FileSystemComponent component : components) {
            System.out.print("组件: ");
            component.showDetails("");
            System.out.println("大小: " + component.getSize() + " bytes");
            System.out.println();
        }
        
        // 尝试向文件添加组件(会抛出异常)
        try {
            file1.add(file2);
        } catch (UnsupportedOperationException e) {
            System.out.println("预期异常: " + e.getMessage());
        }
    }
}

示例2:电商产品目录(更实用的例子)

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

// 1. 组件接口:产品项
interface ProductComponent {
    String getName();
    double getPrice();
    void display(String indent);
    default void add(ProductComponent component) {
        throw new UnsupportedOperationException("不支持添加操作");
    }
    default void remove(ProductComponent component) {
        throw new UnsupportedOperationException("不支持移除操作");
    }
}

// 2. 叶子节点:单个产品
class Product implements ProductComponent {
    private String name;
    private double price;
    
    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public double getPrice() {
        return price;
    }
    
    @Override
    public void display(String indent) {
        System.out.println(indent + "🛒 " + name + " - ¥" + price);
    }
}

// 3. 叶子节点:打折产品(特殊类型的叶子)
class DiscountedProduct implements ProductComponent {
    private Product product;
    private double discount; // 折扣率,如0.8表示8折
    
    public DiscountedProduct(String name, double price, double discount) {
        this.product = new Product(name, price);
        this.discount = discount;
    }
    
    @Override
    public String getName() {
        return product.getName() + " (打折)";
    }
    
    @Override
    public double getPrice() {
        return product.getPrice() * discount;
    }
    
    @Override
    public void display(String indent) {
        System.out.println(indent + "🎁 " + product.getName() + 
                         " - 原价:¥" + product.getPrice() + 
                         ", 折后价:¥" + getPrice());
    }
}

// 4. 容器节点:产品包
class ProductBundle implements ProductComponent {
    private String name;
    private List<ProductComponent> components = new ArrayList<>();
    private double bundleDiscount; // 套装折扣
    
    public ProductBundle(String name, double bundleDiscount) {
        this.name = name;
        this.bundleDiscount = bundleDiscount;
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public double getPrice() {
        double total = 0;
        for (ProductComponent component : components) {
            total += component.getPrice();
        }
        return total * bundleDiscount;
    }
    
    @Override
    public void display(String indent) {
        System.out.println(indent + "📦 " + name + " - 套装价: ¥" + getPrice());
        for (ProductComponent component : components) {
            component.display(indent + "    ");
        }
    }
    
    @Override
    public void add(ProductComponent component) {
        components.add(component);
    }
    
    @Override
    public void remove(ProductComponent component) {
        components.remove(component);
    }
    
    // 计算套装原价(无折扣)
    public double getOriginalPrice() {
        double total = 0;
        for (ProductComponent component : components) {
            total += component.getPrice();
        }
        return total;
    }
}

// 5. 客户端:购物车
public class ShoppingCartDemo {
    public static void main(String[] args) {
        System.out.println("=== 组合模式示例:电商产品目录 ===\n");
        
        // 创建单个产品
        ProductComponent laptop = new Product("笔记本电脑", 6999);
        ProductComponent mouse = new Product("无线鼠标", 199);
        ProductComponent keyboard = new DiscountedProduct("机械键盘", 499, 0.8); // 8折
        ProductComponent headphone = new Product("蓝牙耳机", 399);
        
        // 创建产品套装
        ProductBundle officeBundle = new ProductBundle("办公套装", 0.9); // 9折
        officeBundle.add(laptop);
        officeBundle.add(mouse);
        officeBundle.add(keyboard);
        
        ProductBundle gamingBundle = new ProductBundle("游戏套装", 0.85); // 85折
        gamingBundle.add(laptop);
        gamingBundle.add(new Product("游戏鼠标", 299));
        gamingBundle.add(headphone);
        
        // 创建顶级套餐(套装中可以包含套装)
        ProductBundle ultimateBundle = new ProductBundle("终极套餐", 0.8);
        ultimateBundle.add(officeBundle);
        ultimateBundle.add(gamingBundle);
        ultimateBundle.add(new Product("显示器", 1999));
        
        // 显示所有产品
        System.out.println("=== 产品目录 ===");
        ProductComponent[] products = {
            laptop, mouse, keyboard, headphone,
            officeBundle, gamingBundle, ultimateBundle
        };
        
        for (ProductComponent product : products) {
            product.display("");
            System.out.println();
        }
        
        // 计算购物车总价
        System.out.println("=== 购物车结算 ===");
        List<ProductComponent> cart = new ArrayList<>();
        cart.add(laptop);
        cart.add(mouse);
        cart.add(officeBundle);
        
        double total = 0;
        for (ProductComponent item : cart) {
            System.out.println(item.getName() + ": ¥" + item.getPrice());
            total += item.getPrice();
        }
        
        System.out.println("-------------------");
        System.out.println("购物车总价: ¥" + total);
        
        // 统一操作演示
        System.out.println("\n=== 统一操作演示 ===");
        System.out.println("所有产品名称和价格:");
        for (ProductComponent product : products) {
            System.out.println("- " + product.getName() + ": ¥" + product.getPrice());
        }
    }
}

示例3:更简化的版本(透明方式 vs 安全方式)

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

/**
 * 组合模式的两种实现方式:
 * 1. 透明方式(推荐):在Component接口中声明所有方法(包括管理子组件的方法)
 * 2. 安全方式:只在Composite中声明管理子组件的方法,Leaf不需要实现这些方法
 */

// 透明方式实现
abstract class UIComponent {
    protected String name;
    
    public UIComponent(String name) {
        this.name = name;
    }
    
    // 所有组件都有的操作
    public abstract void render();
    public abstract void add(UIComponent component);
    public abstract void remove(UIComponent component);
    public abstract UIComponent getChild(int index);
}

// 叶子节点:按钮
class Button extends UIComponent {
    public Button(String name) {
        super(name);
    }
    
    @Override
    public void render() {
        System.out.println("渲染按钮: " + name);
    }
    
    @Override
    public void add(UIComponent component) {
        throw new UnsupportedOperationException("按钮不能添加子组件");
    }
    
    @Override
    public void remove(UIComponent component) {
        throw new UnsupportedOperationException("按钮没有子组件");
    }
    
    @Override
    public UIComponent getChild(int index) {
        throw new UnsupportedOperationException("按钮没有子组件");
    }
}

// 容器节点:面板
class Panel extends UIComponent {
    private List<UIComponent> children = new ArrayList<>();
    
    public Panel(String name) {
        super(name);
    }
    
    @Override
    public void render() {
        System.out.println("渲染面板: " + name);
        for (UIComponent child : children) {
            child.render();
        }
    }
    
    @Override
    public void add(UIComponent component) {
        children.add(component);
    }
    
    @Override
    public void remove(UIComponent component) {
        children.remove(component);
    }
    
    @Override
    public UIComponent getChild(int index) {
        return children.get(index);
    }
    
    public int getChildCount() {
        return children.size();
    }
}

public class CompositeSimpleDemo {
    public static void main(String[] args) {
        System.out.println("=== 组合模式简单示例:UI组件 ===\n");
        
        // 创建UI组件
        UIComponent button1 = new Button("确定");
        UIComponent button2 = new Button("取消");
        UIComponent textField = new Button("输入框"); // 简化,实际应该是不同类
        
        // 创建容器
        UIComponent panel1 = new Panel("主面板");
        UIComponent panel2 = new Panel("按钮面板");
        
        // 构建UI树
        panel2.add(button1);
        panel2.add(button2);
        
        panel1.add(textField);
        panel1.add(panel2);
        
        // 统一渲染
        System.out.println("渲染整个UI:");
        panel1.render();
        
        // 统一操作
        System.out.println("\n--- 统一操作演示 ---");
        UIComponent[] components = {button1, panel1, panel2};
        
        for (UIComponent component : components) {
            System.out.println("处理组件: " + component.name);
            component.render();
            System.out.println();
        }
    }
}

组合模式的优缺点

优点:

  1. 简化客户端代码:客户端可以一致地处理单个对象和组合对象
  2. 易于添加新组件类型:新增叶子或容器都很容易,符合开闭原则
  3. 可以方便地构建复杂的树形结构
  4. 提供了清晰的分层结构

缺点:

  1. 设计较复杂:需要仔细设计接口,确保所有组件都支持所有操作
  2. 类型安全问题:在透明方式中,叶子节点需要实现一些无意义的方法(如add/remove)
  3. 难以限制容器中的组件类型:如果不加约束,容器可能包含不合适的组件

使用场景

  1. 需要表示部分-整体层次结构:如文件系统、组织架构、UI组件树
  2. 希望用户忽略组合对象与单个对象的不同:用户可以一致地使用它们
  3. 需要遍历整个结构或部分结构:如计算总价、搜索特定项

组合模式 vs 其他模式

  • 与装饰器模式:装饰器模式增强了单个对象的功能,而组合模式处理对象集合
  • 与迭代器模式:组合模式常与迭代器模式结合使用来遍历组合结构
  • 与访问者模式:访问者模式可以对组合结构中的元素执行操作

组合模式的关键在于一致性:让客户端不用关心处理的是单个对象还是组合对象,从而简化了复杂树形结构的操作。

装饰器设计模式

通俗解释

想象一下去买一杯基础咖啡(原对象),然后可以:

  1. 加牛奶(装饰器1)
  2. 加糖(装饰器2)
  3. 加奶油(装饰器3)
  4. 加巧克力(装饰器4)

每个添加物都会增强改变 咖啡的口味和价格,但核心还是那杯咖啡。装饰器模式就像这些"添加物",它们可以动态地给对象添加新功能,而不改变对象本身的结构。

核心思想:像洋葱一样一层层包裹对象,每一层都添加新功能,但保持接口一致。

关键特点

  • 动态添加功能:运行时添加,而不是编译时
  • 符合开闭原则:对扩展开放,对修改关闭
  • 组合优于继承:避免了通过继承导致的类爆炸
  • 保持接口一致:装饰后的对象和原对象使用相同接口

现实生活例子

  1. Java I/O流BufferedReader(FileReader) 就是装饰器
  2. 咖啡订单系统:基础饮料 + 各种配料
  3. 游戏角色装备:基础角色 + 武器/防具/饰品
  4. Web开发中间件:HTTP请求处理链
  5. GUI组件:带滚动条、边框的文本框

代码示例

示例1:咖啡订单系统(经典例子)

java 复制代码
// 1. 组件接口:饮料
interface Beverage {
    String getDescription();
    double cost();
}

// 2. 具体组件:基础饮料
class Espresso implements Beverage {
    @Override
    public String getDescription() {
        return "浓缩咖啡";
    }
    
    @Override
    public double cost() {
        return 25.0;
    }
}

class HouseBlend implements Beverage {
    @Override
    public String getDescription() {
        return "家常混合咖啡";
    }
    
    @Override
    public double cost() {
        return 20.0;
    }
}

class Decaf implements Beverage {
    @Override
    public String getDescription() {
        return "低咖啡因咖啡";
    }
    
    @Override
    public double cost() {
        return 22.0;
    }
}

// 3. 装饰器抽象类
abstract class CondimentDecorator implements Beverage {
    protected Beverage beverage;  // 被装饰的对象
    
    public CondimentDecorator(Beverage beverage) {
        this.beverage = beverage;
    }
    
    @Override
    public abstract String getDescription();
}

// 4. 具体装饰器:各种配料
class Milk extends CondimentDecorator {
    public Milk(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + ",加牛奶";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 5.0;
    }
}

class Mocha extends CondimentDecorator {
    public Mocha(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + ",加摩卡";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 7.0;
    }
}

class Whip extends CondimentDecorator {
    public Whip(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + ",加奶油";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 4.0;
    }
}

class Soy extends CondimentDecorator {
    public Soy(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + ",加豆浆";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 6.0;
    }
}

// 5. 客户端使用
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        System.out.println("=== 装饰器模式示例:咖啡订单系统 ===\n");
        
        // 1. 单纯一杯浓缩咖啡
        Beverage espresso = new Espresso();
        System.out.println("订单1: " + espresso.getDescription());
        System.out.println("价格: ¥" + espresso.cost());
        
        System.out.println("\n---\n");
        
        // 2. 浓缩咖啡 + 双份摩卡 + 奶油
        Beverage beverage2 = new Espresso();
        beverage2 = new Mocha(beverage2);  // 第一次装饰
        beverage2 = new Mocha(beverage2);  // 第二次装饰
        beverage2 = new Whip(beverage2);   // 第三次装饰
        
        System.out.println("订单2: " + beverage2.getDescription());
        System.out.println("价格: ¥" + beverage2.cost());
        
        System.out.println("\n---\n");
        
        // 3. 低咖啡因咖啡 + 豆浆 + 牛奶 + 奶油
        Beverage beverage3 = new Decaf();
        beverage3 = new Soy(beverage3);
        beverage3 = new Milk(beverage3);
        beverage3 = new Whip(beverage3);
        
        System.out.println("订单3: " + beverage3.getDescription());
        System.out.println("价格: ¥" + beverage3.cost());
        
        System.out.println("\n---\n");
        
        // 4. 家常混合咖啡 + 所有配料!
        Beverage beverage4 = new HouseBlend();
        beverage4 = new Milk(beverage4);
        beverage4 = new Mocha(beverage4);
        beverage4 = new Soy(beverage4);
        beverage4 = new Whip(beverage4);
        
        System.out.println("订单4: " + beverage4.getDescription());
        System.out.println("价格: ¥" + beverage4.cost());
        
        // 演示动态添加功能
        System.out.println("\n=== 动态添加功能演示 ===");
        Beverage simpleCoffee = new HouseBlend();
        System.out.println("初始: " + simpleCoffee.getDescription() + " - ¥" + simpleCoffee.cost());
        
        // 顾客想加牛奶
        simpleCoffee = new Milk(simpleCoffee);
        System.out.println("加牛奶后: " + simpleCoffee.getDescription() + " - ¥" + simpleCoffee.cost());
        
        // 顾客改变主意,想加摩卡而不是牛奶
        // 注意:装饰器是不可逆的,我们需要重新创建
        Beverage newCoffee = new HouseBlend();
        newCoffee = new Mocha(newCoffee);
        System.out.println("改为加摩卡: " + newCoffee.getDescription() + " - ¥" + newCoffee.cost());
    }
}

示例2:游戏角色装备系统

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

// 1. 组件接口:游戏角色
interface GameCharacter {
    String getName();
    int getAttack();
    int getDefense();
    int getHealth();
    List<String> getSkills();
    void showStatus();
}

// 2. 具体组件:基础角色
class Warrior implements GameCharacter {
    @Override
    public String getName() {
        return "战士";
    }
    
    @Override
    public int getAttack() {
        return 10;
    }
    
    @Override
    public int getDefense() {
        return 5;
    }
    
    @Override
    public int getHealth() {
        return 100;
    }
    
    @Override
    public List<String> getSkills() {
        return new ArrayList<>(List.of("基础攻击"));
    }
    
    @Override
    public void showStatus() {
        System.out.println("=== 角色状态 ===");
        System.out.println("职业: " + getName());
        System.out.println("攻击力: " + getAttack());
        System.out.println("防御力: " + getDefense());
        System.out.println("生命值: " + getHealth());
        System.out.println("技能: " + String.join(", ", getSkills()));
    }
}

class Mage implements GameCharacter {
    @Override
    public String getName() {
        return "法师";
    }
    
    @Override
    public int getAttack() {
        return 15;
    }
    
    @Override
    public int getDefense() {
        return 2;
    }
    
    @Override
    public int getHealth() {
        return 80;
    }
    
    @Override
    public List<String> getSkills() {
        return new ArrayList<>(List.of("火球术"));
    }
    
    @Override
    public void showStatus() {
        System.out.println("=== 角色状态 ===");
        System.out.println("职业: " + getName());
        System.out.println("攻击力: " + getAttack());
        System.out.println("防御力: " + getDefense());
        System.out.println("生命值: " + getHealth());
        System.out.println("技能: " + String.join(", ", getSkills()));
    }
}

// 3. 装饰器抽象类:装备
abstract class EquipmentDecorator implements GameCharacter {
    protected GameCharacter character;
    
    public EquipmentDecorator(GameCharacter character) {
        this.character = character;
    }
    
    @Override
    public String getName() {
        return character.getName();
    }
    
    @Override
    public void showStatus() {
        character.showStatus();
    }
}

// 4. 具体装饰器:各种装备
class Sword extends EquipmentDecorator {
    public Sword(GameCharacter character) {
        super(character);
    }
    
    @Override
    public int getAttack() {
        return character.getAttack() + 20;
    }
    
    @Override
    public int getDefense() {
        return character.getDefense() + 5;
    }
    
    @Override
    public int getHealth() {
        return character.getHealth();
    }
    
    @Override
    public List<String> getSkills() {
        List<String> skills = new ArrayList<>(character.getSkills());
        skills.add("剑术精通");
        return skills;
    }
    
    @Override
    public void showStatus() {
        super.showStatus();
        System.out.println("装备: 长剑 (+20攻击, +5防御, 获得剑术精通)");
    }
}

class Shield extends EquipmentDecorator {
    public Shield(GameCharacter character) {
        super(character);
    }
    
    @Override
    public int getAttack() {
        return character.getAttack();
    }
    
    @Override
    public int getDefense() {
        return character.getDefense() + 15;
    }
    
    @Override
    public int getHealth() {
        return character.getHealth() + 20;
    }
    
    @Override
    public List<String> getSkills() {
        List<String> skills = new ArrayList<>(character.getSkills());
        skills.add("盾牌格挡");
        return skills;
    }
    
    @Override
    public void showStatus() {
        super.showStatus();
        System.out.println("装备: 盾牌 (+15防御, +20生命, 获得盾牌格挡)");
    }
}

class MagicWand extends EquipmentDecorator {
    public MagicWand(GameCharacter character) {
        super(character);
    }
    
    @Override
    public int getAttack() {
        return character.getAttack() + 25;
    }
    
    @Override
    public int getDefense() {
        return character.getDefense();
    }
    
    @Override
    public int getHealth() {
        return character.getHealth();
    }
    
    @Override
    public List<String> getSkills() {
        List<String> skills = new ArrayList<>(character.getSkills());
        skills.add("魔法增强");
        skills.add("闪电链");
        return skills;
    }
    
    @Override
    public void showStatus() {
        super.showStatus();
        System.out.println("装备: 魔法杖 (+25攻击, 获得魔法增强和闪电链)");
    }
}

class Armor extends EquipmentDecorator {
    public Armor(GameCharacter character) {
        super(character);
    }
    
    @Override
    public int getAttack() {
        return character.getAttack();
    }
    
    @Override
    public int getDefense() {
        return character.getDefense() + 25;
    }
    
    @Override
    public int getHealth() {
        return character.getHealth() + 50;
    }
    
    @Override
    public List<String> getSkills() {
        List<String> skills = new ArrayList<>(character.getSkills());
        skills.add("伤害减免");
        return skills;
    }
    
    @Override
    public void showStatus() {
        super.showStatus();
        System.out.println("装备: 盔甲 (+25防御, +50生命, 获得伤害减免)");
    }
}

// 5. 客户端:游戏系统
public class GameEquipmentDemo {
    public static void main(String[] args) {
        System.out.println("=== 装饰器模式示例:游戏装备系统 ===\n");
        
        // 创建基础角色
        System.out.println("1. 基础战士:");
        GameCharacter warrior = new Warrior();
        warrior.showStatus();
        
        System.out.println("\n2. 基础法师:");
        GameCharacter mage = new Mage();
        mage.showStatus();
        
        System.out.println("\n=== 装备装备 ===\n");
        
        // 给战士装备武器和防具
        System.out.println("3. 战士装备长剑:");
        GameCharacter warriorWithSword = new Sword(warrior);
        warriorWithSword.showStatus();
        
        System.out.println("\n4. 战士装备长剑和盾牌:");
        GameCharacter warriorWithSwordAndShield = new Shield(warriorWithSword);
        warriorWithSwordAndShield.showStatus();
        
        System.out.println("\n5. 战士全副武装:");
        GameCharacter fullWarrior = new Armor(warriorWithSwordAndShield);
        fullWarrior.showStatus();
        
        System.out.println("\n=== 法师装备 ===\n");
        
        // 给法师装备
        System.out.println("6. 法师装备魔法杖:");
        GameCharacter mageWithWand = new MagicWand(mage);
        mageWithWand.showStatus();
        
        System.out.println("\n7. 法师装备魔法杖和盔甲:");
        GameCharacter mageWithWandAndArmor = new Armor(mageWithWand);
        mageWithWandAndArmor.showStatus();
        
        // 动态更换装备演示
        System.out.println("\n=== 动态更换装备演示 ===\n");
        
        // 创建新战士
        GameCharacter newWarrior = new Warrior();
        System.out.println("新战士创建:");
        newWarrior.showStatus();
        
        // 先装备盾牌
        newWarrior = new Shield(newWarrior);
        System.out.println("\n装备盾牌后:");
        newWarrior.showStatus();
        
        // 再装备长剑(注意顺序)
        newWarrior = new Sword(newWarrior);
        System.out.println("\n再装备长剑后:");
        newWarrior.showStatus();
        
        // 计算总属性
        System.out.println("\n=== 最终属性统计 ===");
        System.out.println("战士最终属性:");
        System.out.println("攻击力: " + fullWarrior.getAttack());
        System.out.println("防御力: " + fullWarrior.getDefense());
        System.out.println("生命值: " + fullWarrior.getHealth());
        System.out.println("技能数: " + fullWarrior.getSkills().size());
        
        System.out.println("\n法师最终属性:");
        System.out.println("攻击力: " + mageWithWandAndArmor.getAttack());
        System.out.println("防御力: " + mageWithWandAndArmor.getDefense());
        System.out.println("生命值: " + mageWithWandAndArmor.getHealth());
        System.out.println("技能数: " + mageWithWandAndArmor.getSkills().size());
    }
}

示例3:文本处理系统(Java I/O风格的例子)

java 复制代码
import java.io.*;

// 1. 组件接口:文本处理器
interface TextProcessor {
    String process(String text);
    void writeToFile(String text, String filename) throws IOException;
}

// 2. 具体组件:基础文本处理器
class BasicTextProcessor implements TextProcessor {
    @Override
    public String process(String text) {
        return text;
    }
    
    @Override
    public void writeToFile(String text, String filename) throws IOException {
        try (FileWriter writer = new FileWriter(filename)) {
            writer.write(text);
        }
    }
}

// 3. 装饰器抽象类
abstract class TextProcessorDecorator implements TextProcessor {
    protected TextProcessor textProcessor;
    
    public TextProcessorDecorator(TextProcessor textProcessor) {
        this.textProcessor = textProcessor;
    }
    
    @Override
    public String process(String text) {
        return textProcessor.process(text);
    }
    
    @Override
    public void writeToFile(String text, String filename) throws IOException {
        textProcessor.writeToFile(text, filename);
    }
}

// 4. 具体装饰器:各种文本处理功能
class UpperCaseDecorator extends TextProcessorDecorator {
    public UpperCaseDecorator(TextProcessor textProcessor) {
        super(textProcessor);
    }
    
    @Override
    public String process(String text) {
        return textProcessor.process(text).toUpperCase();
    }
}

class LowerCaseDecorator extends TextProcessorDecorator {
    public LowerCaseDecorator(TextProcessor textProcessor) {
        super(textProcessor);
    }
    
    @Override
    public String process(String text) {
        return textProcessor.process(text).toLowerCase();
    }
}

class TrimDecorator extends TextProcessorDecorator {
    public TrimDecorator(TextProcessor textProcessor) {
        super(textProcessor);
    }
    
    @Override
    public String process(String text) {
        return textProcessor.process(text).trim();
    }
}

class ReverseDecorator extends TextProcessorDecorator {
    public ReverseDecorator(TextProcessor textProcessor) {
        super(textProcessor);
    }
    
    @Override
    public String process(String text) {
        String processed = textProcessor.process(text);
        return new StringBuilder(processed).reverse().toString();
    }
}

class ReplaceSpaceDecorator extends TextProcessorDecorator {
    private String replacement;
    
    public ReplaceSpaceDecorator(TextProcessor textProcessor, String replacement) {
        super(textProcessor);
        this.replacement = replacement;
    }
    
    @Override
    public String process(String text) {
        return textProcessor.process(text).replace(" ", replacement);
    }
}

class WordCountDecorator extends TextProcessorDecorator {
    public WordCountDecorator(TextProcessor textProcessor) {
        super(textProcessor);
    }
    
    @Override
    public String process(String text) {
        String processed = textProcessor.process(text);
        int wordCount = processed.trim().isEmpty() ? 0 : processed.trim().split("\\s+").length;
        return processed + "\n\n[单词数: " + wordCount + "]";
    }
}

// 5. 客户端使用
public class TextProcessingDemo {
    public static void main(String[] args) {
        System.out.println("=== 装饰器模式示例:文本处理系统 ===\n");
        
        String originalText = "  Hello, World! This is a Decorator Pattern Example.  ";
        
        System.out.println("原始文本: \"" + originalText + "\"");
        System.out.println("长度: " + originalText.length());
        
        System.out.println("\n=== 各种文本处理组合 ===\n");
        
        // 1. 基础处理
        TextProcessor basic = new BasicTextProcessor();
        System.out.println("1. 基础处理: " + basic.process(originalText));
        
        // 2. 修剪 + 大写
        TextProcessor trimAndUpper = new UpperCaseDecorator(new TrimDecorator(basic));
        System.out.println("\n2. 修剪 + 大写: " + trimAndUpper.process(originalText));
        
        // 3. 修剪 + 小写 + 替换空格
        TextProcessor trimLowerReplace = new ReplaceSpaceDecorator(
            new LowerCaseDecorator(
                new TrimDecorator(basic)
            ), "_"
        );
        System.out.println("\n3. 修剪 + 小写 + 替换空格为_: " + trimLowerReplace.process(originalText));
        
        // 4. 修剪 + 反转
        TextProcessor trimAndReverse = new ReverseDecorator(new TrimDecorator(basic));
        System.out.println("\n4. 修剪 + 反转: " + trimAndReverse.process(originalText));
        
        // 5. 完整处理链
        TextProcessor fullProcessing = new WordCountDecorator(
            new ReplaceSpaceDecorator(
                new UpperCaseDecorator(
                    new TrimDecorator(basic)
                ), "---"
            )
        );
        System.out.println("\n5. 完整处理链 (修剪+大写+替换空格+单词统计):");
        System.out.println(fullProcessing.process(originalText));
        
        // 动态组合演示
        System.out.println("\n=== 动态组合演示 ===\n");
        
        TextProcessor processor = new BasicTextProcessor();
        System.out.println("初始: " + processor.process("  Dynamic Processing  "));
        
        // 动态添加功能
        processor = new TrimDecorator(processor);
        System.out.println("加修剪: " + processor.process("  Dynamic Processing  "));
        
        processor = new UpperCaseDecorator(processor);
        System.out.println("加大写: " + processor.process("  Dynamic Processing  "));
        
        processor = new ReverseDecorator(processor);
        System.out.println("加反转: " + processor.process("  Dynamic Processing  "));
        
        // 写入文件示例
        System.out.println("\n=== 文件操作演示 ===\n");
        try {
            TextProcessor fileProcessor = new WordCountDecorator(
                new UpperCaseDecorator(
                    new BasicTextProcessor()
                )
            );
            
            String testText = "This text will be written to a file with uppercase and word count.";
            String processedText = fileProcessor.process(testText);
            
            fileProcessor.writeToFile(processedText, "output.txt");
            System.out.println("文件已生成: output.txt");
            System.out.println("内容预览:\n" + processedText);
            
            // 清理文件
            new File("output.txt").delete();
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

装饰器模式 vs 其他模式

模式 区别
装饰器 vs 继承 装饰器在运行时动态添加功能,继承在编译时静态扩展
装饰器 vs 组合 装饰器增强单个对象功能,组合处理对象集合
装饰器 vs 适配器 装饰器不改变接口,适配器改变接口
装饰器 vs 策略 装饰器添加功能,策略改变算法

装饰器模式的优缺点

优点:

  1. 灵活扩展:比继承更灵活,可以动态添加/移除功能
  2. 避免类爆炸:避免通过继承产生大量子类
  3. 符合开闭原则:新增装饰器无需修改现有代码
  4. 可以多层装饰:通过嵌套实现复杂功能组合

缺点:

  1. 增加复杂性:多层装饰使调试困难
  2. 顺序敏感性:装饰顺序可能影响结果
  3. 对象标识问题:装饰后的对象不等于原对象

实际应用场景

  1. Java I/O库BufferedReaderZipInputStream
  2. Java ServletHttpServletRequestWrapper
  3. Spring框架HandlerInterceptor
  4. 图形界面:带滚动条、边框的组件
  5. Web中间件:身份验证、日志、压缩等处理链

装饰器模式的本质是:用组合替代继承,实现功能的动态扩展。它让代码更加灵活,更易于维护和扩展。

外观设计模式

通俗解释

想象要开一家公司,需要处理很多事情:注册工商、办理税务、开设银行账户、租赁办公室、招聘员工等。如果没有中介,需要分别跑工商局、税务局、银行、房产中介、招聘网站......

外观模式就像一个公司注册代办服务:只需要联系这个代办服务(外观),它就会帮处理所有繁琐的事情。不需要知道每个部门的具体流程,只需要告诉代办服务的需求。

核心思想为一组复杂的子系统提供一个统一的、简化的接口,隐藏子系统的复杂性,让客户端更容易使用。

关键特点

  1. 简化接口:将多个复杂接口转换为一个简单接口
  2. 解耦客户端与子系统:客户端不需要了解子系统内部细节
  3. 提高可维护性:子系统变化时,只需要修改外观类
  4. 易于使用:降低学习成本,让系统更易理解

现实生活例子

  1. 电脑启动按钮:按一下,电脑内部执行BIOS自检、加载操作系统、启动服务等复杂过程
  2. 汽车驾驶:只需要操作方向盘、油门、刹车,不需要知道发动机、变速箱如何工作
  3. 在线购物:点击"购买",系统自动处理库存检查、支付处理、物流安排等
  4. 酒店接待:告诉前台需求,他们帮处理房间、餐饮、清洁等

代码示例

示例1:家庭影院系统(经典例子)

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

// 子系统类:各种设备
class DVDPlayer {
    private String movie;
    
    public void on() {
        System.out.println("DVD播放器: 打开");
    }
    
    public void off() {
        System.out.println("DVD播放器: 关闭");
    }
    
    public void play(String movie) {
        this.movie = movie;
        System.out.println("DVD播放器: 播放电影《" + movie + "》");
    }
    
    public void stop() {
        System.out.println("DVD播放器: 停止播放《" + movie + "》");
    }
    
    public void eject() {
        System.out.println("DVD播放器: 弹出光盘");
    }
}

class Projector {
    public void on() {
        System.out.println("投影仪: 打开");
    }
    
    public void off() {
        System.out.println("投影仪: 关闭");
    }
    
    public void wideScreenMode() {
        System.out.println("投影仪: 设置为宽屏模式");
    }
    
    public void tvMode() {
        System.out.println("投影仪: 设置为电视模式");
    }
}

class SoundSystem {
    private int volume = 5;
    
    public void on() {
        System.out.println("音响系统: 打开");
    }
    
    public void off() {
        System.out.println("音响系统: 关闭");
    }
    
    public void setVolume(int level) {
        volume = level;
        System.out.println("音响系统: 设置音量为 " + level);
    }
    
    public void setSurroundSound() {
        System.out.println("音响系统: 设置为环绕声模式");
    }
    
    public void setStereoSound() {
        System.out.println("音响系统: 设置为立体声模式");
    }
}

class Lights {
    public void dim(int level) {
        System.out.println("灯光: 调暗到 " + level + "%");
    }
    
    public void on() {
        System.out.println("灯光: 打开");
    }
    
    public void off() {
        System.out.println("灯光: 关闭");
    }
}

class Screen {
    public void down() {
        System.out.println("幕布: 降下");
    }
    
    public void up() {
        System.out.println("幕布: 升起");
    }
}

class PopcornMachine {
    public void on() {
        System.out.println("爆米花机: 打开");
    }
    
    public void off() {
        System.out.println("爆米花机: 关闭");
    }
    
    public void pop() {
        System.out.println("爆米花机: 开始制作爆米花");
    }
}

// 外观类:家庭影院外观
class HomeTheaterFacade {
    private DVDPlayer dvdPlayer;
    private Projector projector;
    private SoundSystem soundSystem;
    private Lights lights;
    private Screen screen;
    private PopcornMachine popcornMachine;
    
    public HomeTheaterFacade(DVDPlayer dvdPlayer, 
                             Projector projector, 
                             SoundSystem soundSystem, 
                             Lights lights, 
                             Screen screen, 
                             PopcornMachine popcornMachine) {
        this.dvdPlayer = dvdPlayer;
        this.projector = projector;
        this.soundSystem = soundSystem;
        this.lights = lights;
        this.screen = screen;
        this.popcornMachine = popcornMachine;
    }
    
    // 看电影的简化接口
    public void watchMovie(String movie) {
        System.out.println("🎬 准备看电影《" + movie + "》...");
        
        // 以下步骤原本需要用户分别操作每个设备
        popcornMachine.on();
        popcornMachine.pop();
        
        lights.dim(10);
        
        screen.down();
        
        projector.on();
        projector.wideScreenMode();
        
        soundSystem.on();
        soundSystem.setSurroundSound();
        soundSystem.setVolume(8);
        
        dvdPlayer.on();
        dvdPlayer.play(movie);
        
        System.out.println("✅ 电影《" + movie + "》开始播放,享受观影时光!\n");
    }
    
    // 结束电影的简化接口
    public void endMovie() {
        System.out.println("🛑 结束观影...");
        
        popcornMachine.off();
        
        dvdPlayer.stop();
        dvdPlayer.eject();
        dvdPlayer.off();
        
        soundSystem.off();
        
        projector.off();
        
        screen.up();
        
        lights.on();
        
        System.out.println("✅ 家庭影院已关闭\n");
    }
    
    // 听音乐的简化接口
    public void listenToMusic(String artist, String album) {
        System.out.println("🎵 准备听音乐:" + artist + " - " + album);
        
        lights.dim(30);
        
        soundSystem.on();
        soundSystem.setStereoSound();
        soundSystem.setVolume(6);
        
        System.out.println("✅ 开始播放音乐,享受休闲时光!\n");
    }
    
    // 停止音乐的简化接口
    public void stopMusic() {
        System.out.println("⏹️ 停止音乐播放...");
        
        soundSystem.off();
        lights.on();
        
        System.out.println("✅ 音乐已停止\n");
    }
    
    // 游戏模式的简化接口
    public void playGame(String game) {
        System.out.println("🎮 准备玩游戏:" + game);
        
        lights.dim(50);
        
        projector.on();
        projector.tvMode();
        
        soundSystem.on();
        soundSystem.setSurroundSound();
        soundSystem.setVolume(7);
        
        System.out.println("✅ 游戏模式已就绪,开始游戏吧!\n");
    }
}

// 客户端使用
public class HomeTheaterDemo {
    public static void main(String[] args) {
        System.out.println("=== 外观模式示例:家庭影院系统 ===\n");
        
        // 创建子系统组件
        DVDPlayer dvdPlayer = new DVDPlayer();
        Projector projector = new Projector();
        SoundSystem soundSystem = new SoundSystem();
        Lights lights = new Lights();
        Screen screen = new Screen();
        PopcornMachine popcornMachine = new PopcornMachine();
        
        // 创建外观
        HomeTheaterFacade homeTheater = new HomeTheaterFacade(
            dvdPlayer, projector, soundSystem, lights, screen, popcornMachine
        );
        
        // 使用简化的外观接口
        System.out.println("场景1: 看电影");
        homeTheater.watchMovie("阿凡达");
        homeTheater.endMovie();
        
        System.out.println("场景2: 听音乐");
        homeTheater.listenToMusic("周杰伦", "七里香");
        homeTheater.stopMusic();
        
        System.out.println("场景3: 玩游戏");
        homeTheater.playGame("塞尔达传说");
        
        // 对比:没有外观模式的复杂操作
        System.out.println("=== 对比:没有外观模式的复杂操作 ===");
        System.out.println("如果需要手动操作所有设备看电影,需要执行:");
        System.out.println("1. popcornMachine.on()");
        System.out.println("2. popcornMachine.pop()");
        System.out.println("3. lights.dim(10)");
        System.out.println("4. screen.down()");
        System.out.println("5. projector.on()");
        System.out.println("6. projector.wideScreenMode()");
        System.out.println("7. soundSystem.on()");
        System.out.println("8. soundSystem.setSurroundSound()");
        System.out.println("9. soundSystem.setVolume(8)");
        System.out.println("10. dvdPlayer.on()");
        System.out.println("11. dvdPlayer.play(\"阿凡达\")");
        System.out.println("\n而使用外观模式只需要:homeTheater.watchMovie(\"阿凡达\")");
    }
}

示例2:在线购物系统

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

// 子系统类:库存管理
class InventoryService {
    private Map<String, Integer> inventory = new HashMap<>();
    
    public InventoryService() {
        // 初始化库存
        inventory.put("iPhone14", 10);
        inventory.put("MacBook Pro", 5);
        inventory.put("AirPods", 20);
        inventory.put("iPad", 8);
    }
    
    public boolean checkAvailability(String productId, int quantity) {
        Integer stock = inventory.get(productId);
        if (stock == null) {
            System.out.println("库存服务: 商品 " + productId + " 不存在");
            return false;
        }
        
        if (stock >= quantity) {
            System.out.println("库存服务: 商品 " + productId + " 库存充足 (" + stock + "件)");
            return true;
        } else {
            System.out.println("库存服务: 商品 " + productId + " 库存不足,仅剩 " + stock + "件");
            return false;
        }
    }
    
    public void updateInventory(String productId, int quantity) {
        Integer currentStock = inventory.get(productId);
        if (currentStock != null) {
            inventory.put(productId, currentStock - quantity);
            System.out.println("库存服务: 更新库存," + productId + " 减少 " + quantity + "件,剩余 " + inventory.get(productId) + "件");
        }
    }
}

// 子系统类:支付处理
class PaymentService {
    public boolean processPayment(String userId, double amount, String paymentMethod) {
        System.out.println("支付服务: 处理支付 - 用户: " + userId + 
                          ", 金额: ¥" + amount + 
                          ", 支付方式: " + paymentMethod);
        
        // 模拟支付处理
        boolean success = Math.random() > 0.1; // 90%成功率
        
        if (success) {
            System.out.println("支付服务: 支付成功");
            return true;
        } else {
            System.out.println("支付服务: 支付失败");
            return false;
        }
    }
    
    public void refund(String orderId, double amount) {
        System.out.println("支付服务: 退款处理 - 订单: " + orderId + ", 金额: ¥" + amount);
    }
}

// 子系统类:物流服务
class ShippingService {
    public String createShipment(String orderId, String address, List<String> products) {
        System.out.println("物流服务: 创建发货单 - 订单: " + orderId + 
                          ", 地址: " + address + 
                          ", 商品: " + products);
        
        String trackingNumber = "TRK" + System.currentTimeMillis();
        System.out.println("物流服务: 发货单已创建,运单号: " + trackingNumber);
        
        return trackingNumber;
    }
    
    public void cancelShipment(String trackingNumber) {
        System.out.println("物流服务: 取消发货 - 运单号: " + trackingNumber);
    }
    
    public String trackShipment(String trackingNumber) {
        System.out.println("物流服务: 查询物流信息 - 运单号: " + trackingNumber);
        
        // 模拟物流状态
        String[] statuses = {"已揽收", "运输中", "派送中", "已签收"};
        String status = statuses[(int)(Math.random() * statuses.length)];
        
        return "物流状态: " + status;
    }
}

// 子系统类:通知服务
class NotificationService {
    public void sendEmail(String email, String subject, String content) {
        System.out.println("通知服务: 发送邮件 - 收件人: " + email);
        System.out.println("  主题: " + subject);
        System.out.println("  内容: " + content);
    }
    
    public void sendSMS(String phone, String message) {
        System.out.println("通知服务: 发送短信 - 手机号: " + phone);
        System.out.println("  内容: " + message);
    }
    
    public void sendAppNotification(String userId, String message) {
        System.out.println("通知服务: 发送App通知 - 用户: " + userId);
        System.out.println("  内容: " + message);
    }
}

// 子系统类:订单管理
class OrderService {
    private int orderCounter = 1000;
    
    public String createOrder(String userId, Map<String, Integer> items) {
        String orderId = "ORD" + (orderCounter++);
        
        System.out.println("订单服务: 创建订单 - 订单号: " + orderId + 
                          ", 用户: " + userId + 
                          ", 商品: " + items);
        
        return orderId;
    }
    
    public void cancelOrder(String orderId) {
        System.out.println("订单服务: 取消订单 - 订单号: " + orderId);
    }
    
    public String getOrderStatus(String orderId) {
        System.out.println("订单服务: 查询订单状态 - 订单号: " + orderId);
        
        // 模拟订单状态
        String[] statuses = {"待支付", "已支付", "发货中", "已完成", "已取消"};
        String status = statuses[(int)(Math.random() * statuses.length)];
        
        return "订单状态: " + status;
    }
}

// 外观类:购物系统外观
class ShoppingFacade {
    private InventoryService inventoryService;
    private PaymentService paymentService;
    private ShippingService shippingService;
    private NotificationService notificationService;
    private OrderService orderService;
    
    public ShoppingFacade() {
        this.inventoryService = new InventoryService();
        this.paymentService = new PaymentService();
        this.shippingService = new ShippingService();
        this.notificationService = new NotificationService();
        this.orderService = new OrderService();
    }
    
    // 简化的购物接口
    public ShoppingResult placeOrder(String userId, 
                                     Map<String, Integer> items, 
                                     String address, 
                                     String paymentMethod,
                                     String email,
                                     String phone) {
        System.out.println("🛒 用户 " + userId + " 开始下单...");
        System.out.println("商品: " + items);
        
        // 1. 检查库存
        System.out.println("\n步骤1: 检查库存");
        for (Map.Entry<String, Integer> entry : items.entrySet()) {
            if (!inventoryService.checkAvailability(entry.getKey(), entry.getValue())) {
                return new ShoppingResult(false, "库存不足: " + entry.getKey(), null, null);
            }
        }
        
        // 2. 创建订单
        System.out.println("\n步骤2: 创建订单");
        String orderId = orderService.createOrder(userId, items);
        
        // 3. 处理支付
        System.out.println("\n步骤3: 处理支付");
        double totalAmount = calculateTotalAmount(items);
        boolean paymentSuccess = paymentService.processPayment(userId, totalAmount, paymentMethod);
        
        if (!paymentSuccess) {
            // 支付失败,取消订单
            orderService.cancelOrder(orderId);
            return new ShoppingResult(false, "支付失败", orderId, null);
        }
        
        // 4. 更新库存
        System.out.println("\n步骤4: 更新库存");
        for (Map.Entry<String, Integer> entry : items.entrySet()) {
            inventoryService.updateInventory(entry.getKey(), entry.getValue());
        }
        
        // 5. 创建发货单
        System.out.println("\n步骤5: 安排发货");
        List<String> productList = new ArrayList<>(items.keySet());
        String trackingNumber = shippingService.createShipment(orderId, address, productList);
        
        // 6. 发送通知
        System.out.println("\n步骤6: 发送通知");
        notificationService.sendEmail(email, 
            "订单确认 - " + orderId, 
            "您的订单已确认,运单号: " + trackingNumber + ",商品: " + items);
        
        notificationService.sendSMS(phone, 
            "您的订单" + orderId + "已确认,运单号: " + trackingNumber);
        
        notificationService.sendAppNotification(userId, 
            "订单" + orderId + "支付成功,已安排发货");
        
        System.out.println("\n✅ 下单成功!");
        return new ShoppingResult(true, "下单成功", orderId, trackingNumber);
    }
    
    // 简化的订单查询接口
    public void trackOrder(String orderId, String email, String phone) {
        System.out.println("📦 查询订单状态: " + orderId);
        
        String orderStatus = orderService.getOrderStatus(orderId);
        System.out.println("  " + orderStatus);
        
        // 如果有运单号,查询物流
        String trackingNumber = "TRK" + orderId.substring(3); // 模拟生成运单号
        String shippingStatus = shippingService.trackShipment(trackingNumber);
        System.out.println("  " + shippingStatus);
        
        // 发送状态通知
        notificationService.sendEmail(email, 
            "订单状态更新 - " + orderId, 
            orderStatus + "\n" + shippingStatus);
    }
    
    // 简化的取消订单接口
    public void cancelOrder(String orderId, String reason, String email, String phone) {
        System.out.println("❌ 取消订单: " + orderId + ",原因: " + reason);
        
        // 1. 取消订单
        orderService.cancelOrder(orderId);
        
        // 2. 退款处理
        paymentService.refund(orderId, 999.99); // 模拟金额
        
        // 3. 取消发货
        String trackingNumber = "TRK" + orderId.substring(3);
        shippingService.cancelShipment(trackingNumber);
        
        // 4. 发送通知
        notificationService.sendEmail(email, 
            "订单取消通知 - " + orderId, 
            "您的订单已取消,原因: " + reason + ",退款将在3-7个工作日内处理。");
        
        notificationService.sendSMS(phone, 
            "订单" + orderId + "已取消,退款处理中");
        
        System.out.println("✅ 订单取消完成");
    }
    
    // 简化的退货接口
    public void returnOrder(String orderId, String reason, String email) {
        System.out.println("🔄 处理退货: " + orderId + ",原因: " + reason);
        
        // 1. 退款处理
        paymentService.refund(orderId, 999.99); // 模拟金额
        
        // 2. 发送通知
        notificationService.sendEmail(email, 
            "退货处理完成 - " + orderId, 
            "您的退货申请已处理,退款将在3-7个工作日内到账。");
        
        System.out.println("✅ 退货处理完成");
    }
    
    private double calculateTotalAmount(Map<String, Integer> items) {
        // 模拟商品价格
        Map<String, Double> prices = new HashMap<>();
        prices.put("iPhone14", 6999.0);
        prices.put("MacBook Pro", 12999.0);
        prices.put("AirPods", 1299.0);
        prices.put("iPad", 3999.0);
        
        double total = 0;
        for (Map.Entry<String, Integer> entry : items.entrySet()) {
            Double price = prices.get(entry.getKey());
            if (price != null) {
                total += price * entry.getValue();
            }
        }
        
        return total;
    }
}

// 封装购物结果
class ShoppingResult {
    private boolean success;
    private String message;
    private String orderId;
    private String trackingNumber;
    
    public ShoppingResult(boolean success, String message, String orderId, String trackingNumber) {
        this.success = success;
        this.message = message;
        this.orderId = orderId;
        this.trackingNumber = trackingNumber;
    }
    
    // getters
    public boolean isSuccess() { return success; }
    public String getMessage() { return message; }
    public String getOrderId() { return orderId; }
    public String getTrackingNumber() { return trackingNumber; }
}

// 客户端使用
public class ShoppingSystemDemo {
    public static void main(String[] args) {
        System.out.println("=== 外观模式示例:在线购物系统 ===\n");
        
        // 创建购物外观
        ShoppingFacade shoppingFacade = new ShoppingFacade();
        
        // 用户下单
        System.out.println("场景1: 用户下单\n");
        Map<String, Integer> order1 = new HashMap<>();
        order1.put("iPhone14", 1);
        order1.put("AirPods", 1);
        
        ShoppingResult result = shoppingFacade.placeOrder(
            "user123", 
            order1, 
            "北京市海淀区中关村大街1号", 
            "支付宝", 
            "user123@example.com", 
            "13800138000"
        );
        
        if (result.isSuccess()) {
            System.out.println("\n下单成功!");
            System.out.println("订单号: " + result.getOrderId());
            System.out.println("运单号: " + result.getTrackingNumber());
        }
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        // 查询订单状态
        System.out.println("场景2: 查询订单状态\n");
        shoppingFacade.trackOrder(
            "ORD1000", 
            "user123@example.com", 
            "13800138000"
        );
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        // 取消订单
        System.out.println("场景3: 取消订单\n");
        shoppingFacade.cancelOrder(
            "ORD1001", 
            "改变主意", 
            "user456@example.com", 
            "13900139000"
        );
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        // 对比:没有外观模式的复杂操作
        System.out.println("=== 对比:没有外观模式的复杂操作 ===");
        System.out.println("如果用户需要手动完成购物流程,需要:");
        System.out.println("1. 调用 inventoryService.checkAvailability() 检查每个商品库存");
        System.out.println("2. 调用 orderService.createOrder() 创建订单");
        System.out.println("3. 调用 paymentService.processPayment() 处理支付");
        System.out.println("4. 调用 inventoryService.updateInventory() 更新库存");
        System.out.println("5. 调用 shippingService.createShipment() 创建发货单");
        System.out.println("6. 调用 notificationService.sendEmail() 发送邮件通知");
        System.out.println("7. 调用 notificationService.sendSMS() 发送短信通知");
        System.out.println("8. 调用 notificationService.sendAppNotification() 发送App通知");
        System.out.println("\n而使用外观模式只需要:shoppingFacade.placeOrder(...)");
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        // 测试退货流程
        System.out.println("场景4: 退货处理\n");
        shoppingFacade.returnOrder(
            "ORD1002", 
            "商品有瑕疵", 
            "user789@example.com"
        );
    }
}

示例3:更简化的电脑启动系统

java 复制代码
// 子系统类
class CPU {
    public void start() {
        System.out.println("CPU: 启动...");
        System.out.println("CPU: 初始化寄存器...");
        System.out.println("CPU: 启动完成");
    }
}

class Memory {
    public void load() {
        System.out.println("内存: 加载数据...");
        System.out.println("内存: 自检完成");
    }
}

class HardDrive {
    public void read() {
        System.out.println("硬盘: 读取引导扇区...");
        System.out.println("硬盘: 加载操作系统文件...");
    }
}

class BIOS {
    public void selfTest() {
        System.out.println("BIOS: 执行开机自检(POST)...");
        System.out.println("BIOS: 检查硬件设备...");
        System.out.println("BIOS: 自检完成,一切正常");
    }
}

class OperatingSystem {
    public void boot() {
        System.out.println("操作系统: 启动内核...");
        System.out.println("操作系统: 初始化设备驱动...");
        System.out.println("操作系统: 启动系统服务...");
        System.out.println("操作系统: 启动完成,欢迎使用!");
    }
}

// 外观类:电脑启动外观
class ComputerFacade {
    private CPU cpu;
    private Memory memory;
    private HardDrive hardDrive;
    private BIOS bios;
    private OperatingSystem os;
    
    public ComputerFacade() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.hardDrive = new HardDrive();
        this.bios = new BIOS();
        this.os = new OperatingSystem();
    }
    
    // 简化的启动接口
    public void startComputer() {
        System.out.println("💻 正在启动电脑...\n");
        
        bios.selfTest();
        System.out.println();
        
        cpu.start();
        System.out.println();
        
        memory.load();
        System.out.println();
        
        hardDrive.read();
        System.out.println();
        
        os.boot();
        
        System.out.println("\n✅ 电脑启动完成,可以开始使用了!");
    }
    
    // 简化的关机接口
    public void shutdownComputer() {
        System.out.println("🛑 正在关闭电脑...\n");
        
        System.out.println("操作系统: 保存所有工作...");
        System.out.println("操作系统: 关闭所有应用程序...");
        System.out.println("操作系统: 停止系统服务...");
        System.out.println("硬件: 关闭电源...");
        
        System.out.println("\n✅ 电脑已关闭");
    }
    
    // 简化的重启接口
    public void restartComputer() {
        System.out.println("🔄 正在重启电脑...\n");
        shutdownComputer();
        System.out.println("\n--- 重启中 ---\n");
        startComputer();
    }
}

// 客户端使用
public class ComputerSystemDemo {
    public static void main(String[] args) {
        System.out.println("=== 外观模式示例:电脑启动系统 ===\n");
        
        // 创建电脑外观
        ComputerFacade computer = new ComputerFacade();
        
        // 用户只需要按一个按钮
        System.out.println("用户按下电源按钮:");
        computer.startComputer();
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        System.out.println("用户选择重启:");
        computer.restartComputer();
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        // 对比:没有外观模式的复杂操作
        System.out.println("=== 对比:没有外观模式的复杂操作 ===");
        System.out.println("如果没有外观模式,用户需要:");
        System.out.println("1. 调用 bios.selfTest()");
        System.out.println("2. 调用 cpu.start()");
        System.out.println("3. 调用 memory.load()");
        System.out.println("4. 调用 hardDrive.read()");
        System.out.println("5. 调用 os.boot()");
        System.out.println("\n而使用外观模式只需要:computer.startComputer()");
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        System.out.println("用户选择关机:");
        computer.shutdownComputer();
    }
}

外观模式的优缺点

优点:

  1. 简化客户端代码:客户端不需要了解复杂子系统
  2. 降低耦合度:子系统变化不会直接影响客户端
  3. 提高安全性:可以限制客户端对子系统的直接访问
  4. 易于使用:提供了一个清晰的、高级的接口
  5. 符合单一职责原则:外观类专门负责简化接口

缺点:

  1. 可能成为上帝对象:如果外观类过于庞大,会承担太多职责
  2. 增加了一层抽象:可能稍微影响性能
  3. 可能隐藏了有用的功能:过于简化的接口可能无法使用子系统的所有功能

使用场景

  1. 复杂子系统需要简化接口:如框架、库的API设计
  2. 需要解耦客户端和子系统:如多层架构中的层间通信
  3. 需要为子系统提供统一入口:如微服务网关
  4. 遗留系统集成:为新系统提供统一接口访问老系统

外观模式 vs 其他模式

模式 区别
外观 vs 适配器 外观简化接口,适配器转换接口
外观 vs 中介者 外观单向简化,中介者多向协调
外观 vs 单例 外观可以有多个实例,单例只有一个
外观 vs 代理 外观简化接口,代理控制访问

实际应用

  1. Spring框架JdbcTemplate 封装了JDBC的复杂操作
  2. SLF4J日志门面:统一了不同日志框架的接口
  3. Servlet APIHttpServletRequestHttpServletResponse 封装了HTTP细节
  4. JDBC驱动管理器DriverManager 简化了数据库连接
  5. Android Context:封装了应用环境信息访问

外观模式的核心价值在于:为复杂系统提供一个简单、统一的入口,让用户能够更容易地使用系统功能,而无需了解内部复杂实现。

享元设计模式

通俗解释

想象一下正在开发一个大型多人在线游戏,游戏中有成千上万棵树。如果每棵树都创建一个独立的对象(包含位置、颜色、纹理等所有属性),内存很快就会耗尽。

享元模式就像"共享单车"系统:与其为每个用户制造一辆新车,不如共享已有的自行车。同样,享元模式通过共享相似对象来减少内存使用。

核心思想共享大量细粒度对象,减少内存占用。将对象的"固有状态"(不变的部分)与"外部状态"(变化的部分)分离,只存储一份固有状态,通过参数传递外部状态。

关键特点

  1. 减少内存使用:共享相同部分,避免重复存储
  2. 分离内部/外部状态
    • 内部状态:对象共享的不变部分(如树的纹理、颜色)
    • 外部状态:对象特有的变化部分(如树的位置、大小)
  3. 工厂管理共享:使用工厂创建和管理共享对象
  4. 适合大量对象场景:当系统中存在大量相似对象时特别有效

现实生活例子

  1. 文字处理器:每个字符共享字体、颜色等属性,只存储一次
  2. 游戏开发:树木、云朵、子弹等大量重复对象
  3. 数据库连接池:共享数据库连接对象
  4. 浏览器标签页:共享浏览器内核实例
  5. 棋类游戏:棋盘上的棋子共享形状、颜色

代码示例

示例1:文字编辑器中的字符处理(经典例子)

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

// 1. 享元接口:字符
interface Character {
    void display(int positionX, int positionY, String color);
}

// 2. 具体享元:具体字符
class ConcreteCharacter implements Character {
    private char symbol;  // 内部状态:字符本身(不变)
    private String font;  // 内部状态:字体(不变)
    private int size;     // 内部状态:字号(不变)
    
    public ConcreteCharacter(char symbol, String font, int size) {
        this.symbol = symbol;
        this.font = font;
        this.size = size;
        System.out.println("创建字符对象: " + symbol + " (字体: " + font + ", 字号: " + size + ")");
    }
    
    @Override
    public void display(int positionX, int positionY, String color) {
        // 外部状态:位置和颜色由客户端传入
        System.out.println("显示字符 '" + symbol + 
                         "' 在位置(" + positionX + "," + positionY + 
                         "), 颜色: " + color + 
                         ", 字体: " + font + 
                         ", 字号: " + size);
    }
    
    // 内部状态getters(用于比较)
    public char getSymbol() { return symbol; }
    public String getFont() { return font; }
    public int getSize() { return size; }
}

// 3. 享元工厂:管理共享的字符对象
class CharacterFactory {
    private static Map<String, Character> characterPool = new HashMap<>();
    
    public static Character getCharacter(char symbol, String font, int size) {
        // 创建唯一标识符
        String key = symbol + "_" + font + "_" + size;
        
        // 如果池中已有,直接返回
        if (characterPool.containsKey(key)) {
            System.out.println("从池中获取字符: " + symbol);
            return characterPool.get(key);
        }
        
        // 否则创建新对象并放入池中
        Character character = new ConcreteCharacter(symbol, font, size);
        characterPool.put(key, character);
        return character;
    }
    
    public static int getPoolSize() {
        return characterPool.size();
    }
    
    public static void showPool() {
        System.out.println("\n字符池内容 (" + characterPool.size() + "个唯一字符):");
        for (String key : characterPool.keySet()) {
            System.out.println("  " + key);
        }
    }
}

// 4. 客户端:文档类,存储字符及其外部状态
class Document {
    private List<CharacterEntry> characters = new ArrayList<>();
    
    // 内部类,存储字符引用和外部状态
    static class CharacterEntry {
        Character character;
        int positionX;
        int positionY;
        String color;
        
        CharacterEntry(Character character, int positionX, int positionY, String color) {
            this.character = character;
            this.positionX = positionX;
            this.positionY = positionY;
            this.color = color;
        }
    }
    
    public void addCharacter(char symbol, String font, int size, 
                            int positionX, int positionY, String color) {
        Character character = CharacterFactory.getCharacter(symbol, font, size);
        characters.add(new CharacterEntry(character, positionX, positionY, color));
    }
    
    public void display() {
        System.out.println("\n=== 文档内容 ===");
        for (CharacterEntry entry : characters) {
            entry.character.display(entry.positionX, entry.positionY, entry.color);
        }
    }
    
    public int getCharacterCount() {
        return characters.size();
    }
}

// 5. 主程序
public class TextEditorDemo {
    public static void main(String[] args) {
        System.out.println("=== 享元模式示例:文字编辑器 ===\n");
        
        // 创建文档
        Document document = new Document();
        
        System.out.println("向文档添加字符...\n");
        
        // 添加大量字符,很多是重复的
        // 第一行: Hello World!
        document.addCharacter('H', "Arial", 12, 10, 10, "黑色");
        document.addCharacter('e', "Arial", 12, 20, 10, "黑色");
        document.addCharacter('l', "Arial", 12, 30, 10, "黑色");
        document.addCharacter('l', "Arial", 12, 40, 10, "黑色");  // 重复的'l'
        document.addCharacter('o', "Arial", 12, 50, 10, "黑色");
        document.addCharacter(' ', "Arial", 12, 60, 10, "黑色");
        document.addCharacter('W', "Arial", 12, 70, 10, "黑色");
        document.addCharacter('o', "Arial", 12, 80, 10, "黑色");  // 重复的'o'
        document.addCharacter('r', "Arial", 12, 90, 10, "黑色");
        document.addCharacter('l', "Arial", 12, 100, 10, "黑色"); // 重复的'l'
        document.addCharacter('d', "Arial", 12, 110, 10, "黑色");
        document.addCharacter('!', "Arial", 12, 120, 10, "红色");
        
        // 第二行: 重复的字符测试
        document.addCharacter('a', "Times New Roman", 14, 10, 30, "蓝色");
        document.addCharacter('a', "Times New Roman", 14, 25, 30, "蓝色"); // 完全相同的字符
        document.addCharacter('a', "Arial", 14, 40, 30, "蓝色");          // 字体不同,不是完全相同的字符
        document.addCharacter('a', "Times New Roman", 16, 55, 30, "蓝色"); // 字号不同,不是完全相同的字符
        
        // 显示文档
        document.display();
        
        // 显示统计信息
        System.out.println("\n=== 内存使用统计 ===");
        System.out.println("文档中字符总数: " + document.getCharacterCount());
        System.out.println("实际创建的字符对象数: " + CharacterFactory.getPoolSize());
        System.out.println("节省的内存: " + 
            (document.getCharacterCount() - CharacterFactory.getPoolSize()) + 
            " 个对象");
        
        // 显示字符池内容
        CharacterFactory.showPool();
        
        // 性能对比:传统方式 vs 享元模式
        System.out.println("\n=== 性能对比 ===");
        
        // 传统方式:每个字符都创建新对象
        long startTime = System.currentTimeMillis();
        List<Character> traditionalList = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            // 模拟添加字符,其中很多重复
            char c = (char)('a' + (i % 26)); // 26个字母循环
            traditionalList.add(new ConcreteCharacter(c, "Arial", 12));
        }
        long traditionalTime = System.currentTimeMillis() - startTime;
        System.out.println("传统方式创建10000个字符对象用时: " + traditionalTime + "ms");
        System.out.println("传统方式创建的对象数: " + traditionalList.size());
        
        // 享元模式
        startTime = System.currentTimeMillis();
        Document largeDocument = new Document();
        for (int i = 0; i < 10000; i++) {
            char c = (char)('a' + (i % 26));
            largeDocument.addCharacter(c, "Arial", 12, i*10, 50, "黑色");
        }
        long flyweightTime = System.currentTimeMillis() - startTime;
        System.out.println("享元模式处理10000个字符用时: " + flyweightTime + "ms");
        System.out.println("享元模式实际创建的对象数: " + CharacterFactory.getPoolSize());
        System.out.println("内存节省: " + (10000 - CharacterFactory.getPoolSize()) + " 个对象");
    }
}

示例2:游戏中的树木渲染(实用例子)

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

// 1. 享元接口:树
interface Tree {
    void render(int x, int y, int height);
}

// 2. 具体享元:树的类型(内部状态)
class TreeType implements Tree {
    private String name;        // 内部状态:树种名称
    private String texture;     // 内部状态:纹理
    private String color;       // 内部状态:颜色
    
    public TreeType(String name, String texture, String color) {
        this.name = name;
        this.texture = texture;
        this.color = color;
        System.out.println("创建树类型: " + name + " (纹理: " + texture + ", 颜色: " + color + ")");
    }
    
    @Override
    public void render(int x, int y, int height) {
        // 外部状态:位置和高度由客户端传入
        System.out.println("渲染 " + name + 
                         " 在位置(" + x + "," + y + 
                         "), 高度: " + height + 
                         "米, 颜色: " + color);
        // 在实际游戏中,这里会调用图形API渲染
    }
    
    // 获取内部状态(用于比较)
    public String getName() { return name; }
    public String getTexture() { return texture; }
    public String getColor() { return color; }
}

// 3. 享元工厂:树类型工厂
class TreeFactory {
    private static Map<String, TreeType> treeTypes = new HashMap<>();
    
    public static TreeType getTreeType(String name, String texture, String color) {
        String key = name + "_" + texture + "_" + color;
        
        if (treeTypes.containsKey(key)) {
            return treeTypes.get(key);
        }
        
        TreeType type = new TreeType(name, texture, color);
        treeTypes.put(key, type);
        return type;
    }
    
    public static int getTypeCount() {
        return treeTypes.size();
    }
    
    public static void showTypes() {
        System.out.println("\n树类型池 (" + treeTypes.size() + "种类型):");
        for (TreeType type : treeTypes.values()) {
            System.out.println("  " + type.getName() + " - " + type.getColor());
        }
    }
}

// 4. 游戏中的具体树(包含外部状态)
class GameTree {
    private TreeType type;  // 共享的内部状态
    private int x, y;       // 外部状态:位置
    private int height;     // 外部状态:高度
    
    public GameTree(String name, String texture, String color, int x, int y, int height) {
        this.type = TreeFactory.getTreeType(name, texture, color);
        this.x = x;
        this.y = y;
        this.height = height;
    }
    
    public void render() {
        type.render(x, y, height);
    }
    
    // 可以改变外部状态
    public void move(int newX, int newY) {
        this.x = newX;
        this.y = newY;
    }
    
    public void grow(int additionalHeight) {
        this.height += additionalHeight;
    }
    
    // getters
    public int getX() { return x; }
    public int getY() { return y; }
    public int getHeight() { return height; }
}

// 5. 游戏森林:管理大量树木
class Forest {
    private List<GameTree> trees = new ArrayList<>();
    private Random random = new Random();
    
    // 预定义的树类型
    private static final String[] TREE_NAMES = {"松树", "橡树", "枫树", "白桦", "柳树"};
    private static final String[] TEXTURES = {"针叶", "宽叶", "红叶", "白皮", "垂枝"};
    private static final String[] COLORS = {"深绿", "浅绿", "红色", "白色", "黄色"};
    
    public void plantRandomTree(int areaWidth, int areaHeight) {
        String name = TREE_NAMES[random.nextInt(TREE_NAMES.length)];
        String texture = TEXTURES[random.nextInt(TEXTURES.length)];
        String color = COLORS[random.nextInt(COLORS.length)];
        
        int x = random.nextInt(areaWidth);
        int y = random.nextInt(areaHeight);
        int height = 5 + random.nextInt(20); // 5-25米
        
        trees.add(new GameTree(name, texture, color, x, y, height));
    }
    
    public void plantTree(String name, String texture, String color, 
                         int x, int y, int height) {
        trees.add(new GameTree(name, texture, color, x, y, height));
    }
    
    public void renderForest() {
        System.out.println("\n=== 渲染森林 (" + trees.size() + "棵树) ===");
        for (GameTree tree : trees) {
            tree.render();
        }
    }
    
    public void growAllTrees(int growth) {
        for (GameTree tree : trees) {
            tree.grow(growth);
        }
    }
    
    public int getTreeCount() {
        return trees.size();
    }
    
    // 统计不同位置的树
    public Map<String, Integer> countByType() {
        Map<String, Integer> counts = new HashMap<>();
        // 注意:这里简化处理,实际需要更多逻辑来识别相同类型
        for (GameTree tree : trees) {
            String key = tree.getX() + "," + tree.getY();
            counts.put(key, counts.getOrDefault(key, 0) + 1);
        }
        return counts;
    }
}

// 6. 主程序
public class GameForestDemo {
    public static void main(String[] args) {
        System.out.println("=== 享元模式示例:游戏森林 ===\n");
        
        // 创建森林
        Forest forest = new Forest();
        
        System.out.println("种植树木...\n");
        
        // 种植一些特定树木
        forest.plantTree("松树", "针叶", "深绿", 10, 20, 15);
        forest.plantTree("橡树", "宽叶", "浅绿", 30, 40, 20);
        forest.plantTree("松树", "针叶", "深绿", 50, 60, 18); // 相同类型,不同位置
        forest.plantTree("枫树", "红叶", "红色", 70, 80, 12);
        forest.plantTree("松树", "针叶", "深绿", 90, 100, 16); // 相同类型,不同位置
        forest.plantTree("橡树", "宽叶", "浅绿", 110, 120, 22); // 相同类型,不同位置
        
        // 随机种植大量树木
        System.out.println("随机种植1000棵树...");
        for (int i = 0; i < 1000; i++) {
            forest.plantRandomTree(1000, 1000);
        }
        
        // 渲染森林
        forest.renderForest();
        
        // 显示统计信息
        System.out.println("\n=== 内存使用统计 ===");
        System.out.println("森林中树木总数: " + forest.getTreeCount());
        System.out.println("实际创建的树类型对象数: " + TreeFactory.getTypeCount());
        System.out.println("节省的内存: " + 
            (forest.getTreeCount() - TreeFactory.getTypeCount()) + 
            " 个对象");
        
        // 显示树类型池
        TreeFactory.showTypes();
        
        // 模拟游戏更新:所有树生长
        System.out.println("\n=== 游戏更新:经过一年 ===");
        forest.growAllTrees(2);
        
        // 重新渲染
        System.out.println("\n重新渲染森林...");
        // forest.renderForest(); // 注释掉避免输出太多
        
        // 性能对比
        System.out.println("\n=== 性能对比 ===");
        
        // 传统方式
        long startTime = System.currentTimeMillis();
        List<TreeType> traditionalTrees = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            // 模拟大量树对象,很多重复
            String name = TREE_NAMES[i % TREE_NAMES.length];
            traditionalTrees.add(new TreeType(name, "default", "green"));
        }
        long traditionalTime = System.currentTimeMillis() - startTime;
        System.out.println("传统方式创建10000棵树对象用时: " + traditionalTime + "ms");
        System.out.println("传统方式创建的对象数: " + traditionalTrees.size());
        
        // 享元模式
        startTime = System.currentTimeMillis();
        Forest largeForest = new Forest();
        for (int i = 0; i < 10000; i++) {
            largeForest.plantRandomTree(10000, 10000);
        }
        long flyweightTime = System.currentTimeMillis() - startTime;
        System.out.println("享元模式处理10000棵树用时: " + flyweightTime + "ms");
        System.out.println("享元模式实际创建的类型对象数: " + TreeFactory.getTypeCount());
        
        // 内存占用估算
        System.out.println("\n=== 内存占用估算 ===");
        int treeObjectSize = 100; // 假设每个树对象100字节
        int typeObjectSize = 200; // 假设每个类型对象200字节
        
        int traditionalMemory = 10000 * treeObjectSize;
        int flyweightMemory = TreeFactory.getTypeCount() * typeObjectSize + 
                              10000 * 12; // 外部状态(3个int,每个4字节)
        
        System.out.println("传统方式内存占用: " + traditionalMemory + " 字节");
        System.out.println("享元模式内存占用: " + flyweightMemory + " 字节");
        System.out.println("内存节省: " + (traditionalMemory - flyweightMemory) + " 字节 (" + 
            ((traditionalMemory - flyweightMemory) * 100 / traditionalMemory) + "%)");
    }
}

示例3:更简化的例子:棋类游戏

java 复制代码
import java.util.HashMap;
import java.util.Map;

// 1. 享元接口:棋子
interface ChessPiece {
    void draw(int x, int y);
}

// 2. 具体享元:棋子类型
class PieceType implements ChessPiece {
    private String name;    // 内部状态:棋子名称
    private String color;   // 内部状态:颜色
    private String symbol;  // 内部状态:符号
    
    public PieceType(String name, String color, String symbol) {
        this.name = name;
        this.color = color;
        this.symbol = symbol;
        System.out.println("创建棋子类型: " + name + " (" + color + ")");
    }
    
    @Override
    public void draw(int x, int y) {
        System.out.println("在位置(" + x + "," + y + ")绘制" + color + "的" + name + symbol);
    }
}

// 3. 享元工厂
class PieceFactory {
    private static Map<String, PieceType> pieces = new HashMap<>();
    
    public static PieceType getPiece(String name, String color) {
        String key = name + "_" + color;
        
        if (pieces.containsKey(key)) {
            return pieces.get(key);
        }
        
        // 根据棋子和颜色确定符号
        String symbol = getSymbol(name);
        PieceType piece = new PieceType(name, color, symbol);
        pieces.put(key, piece);
        return piece;
    }
    
    private static String getSymbol(String name) {
        switch(name) {
            case "王": return "♔";
            case "后": return "♕";
            case "车": return "♖";
            case "象": return "♗";
            case "马": return "♘";
            case "兵": return "♙";
            default: return "?";
        }
    }
    
    public static int getPieceTypeCount() {
        return pieces.size();
    }
}

// 4. 棋盘上的具体棋子
class BoardPiece {
    private PieceType type;  // 共享的内部状态
    private int x, y;        // 外部状态:位置
    
    public BoardPiece(String name, String color, int x, int y) {
        this.type = PieceFactory.getPiece(name, color);
        this.x = x;
        this.y = y;
    }
    
    public void draw() {
        type.draw(x, y);
    }
    
    public void move(int newX, int newY) {
        this.x = newX;
        this.y = newY;
        System.out.println("移动棋子到(" + newX + "," + newY + ")");
    }
}

// 5. 棋盘
class ChessBoard {
    private BoardPiece[][] board = new BoardPiece[8][8];
    
    public void setup() {
        System.out.println("=== 设置国际象棋棋盘 ===");
        
        // 白方
        placePiece("车", "白", 0, 0);
        placePiece("马", "白", 1, 0);
        placePiece("象", "白", 2, 0);
        placePiece("后", "白", 3, 0);
        placePiece("王", "白", 4, 0);
        placePiece("象", "白", 5, 0);
        placePiece("马", "白", 6, 0);
        placePiece("车", "白", 7, 0);
        
        for (int i = 0; i < 8; i++) {
            placePiece("兵", "白", i, 1);
        }
        
        // 黑方
        placePiece("车", "黑", 0, 7);
        placePiece("马", "黑", 1, 7);
        placePiece("象", "黑", 2, 7);
        placePiece("后", "黑", 3, 7);
        placePiece("王", "黑", 4, 7);
        placePiece("象", "黑", 5, 7);
        placePiece("马", "黑", 6, 7);
        placePiece("车", "黑", 7, 7);
        
        for (int i = 0; i < 8; i++) {
            placePiece("兵", "黑", i, 6);
        }
    }
    
    private void placePiece(String name, String color, int x, int y) {
        board[x][y] = new BoardPiece(name, color, x, y);
    }
    
    public void display() {
        System.out.println("\n=== 棋盘显示 ===");
        for (int y = 7; y >= 0; y--) {
            System.out.print(y + " ");
            for (int x = 0; x < 8; x++) {
                if (board[x][y] != null) {
                    System.out.print("P "); // P表示有棋子
                } else {
                    System.out.print(". ");
                }
            }
            System.out.println();
        }
        System.out.println("  0 1 2 3 4 5 6 7");
    }
    
    public void drawAllPieces() {
        System.out.println("\n=== 绘制所有棋子 ===");
        for (int y = 7; y >= 0; y--) {
            for (int x = 0; x < 8; x++) {
                if (board[x][y] != null) {
                    board[x][y].draw();
                }
            }
        }
    }
    
    // 模拟移动
    public void simulateMove() {
        System.out.println("\n=== 模拟棋步 ===");
        if (board[4][1] != null) { // 白兵
            board[4][1].move(4, 3);
            board[4][3] = board[4][1];
            board[4][1] = null;
        }
    }
}

// 6. 主程序
public class ChessGameDemo {
    public static void main(String[] args) {
        System.out.println("=== 享元模式示例:国际象棋 ===\n");
        
        ChessBoard board = new ChessBoard();
        board.setup();
        board.display();
        board.drawAllPieces();
        
        System.out.println("\n=== 内存统计 ===");
        System.out.println("棋盘上棋子总数: 32");
        System.out.println("实际创建的棋子类型对象数: " + PieceFactory.getPieceTypeCount());
        
        // 假设有多个棋盘
        System.out.println("\n=== 多棋盘场景 ===");
        System.out.println("如果有10个棋盘:");
        System.out.println("传统方式需要: 10 * 32 = 320 个棋子对象");
        System.out.println("享元模式需要: " + PieceFactory.getPieceTypeCount() + 
                         " 个类型对象 + 10 * 32 个位置信息");
        System.out.println("节省了 " + (320 - PieceFactory.getPieceTypeCount()) + 
                         " 个重复的类型对象");
        
        // 模拟棋步
        board.simulateMove();
        board.display();
    }
}

享元模式的优缺点

优点:

  1. 大幅减少内存使用:共享相同部分,避免重复对象
  2. 提高性能:减少对象创建和垃圾回收开销
  3. 支持大量对象:可以处理传统方式无法处理的大量对象
  4. 集中管理共享状态:便于统一管理和修改

缺点:

  1. 增加系统复杂性:需要区分内部状态和外部状态
  2. 可能引入线程安全问题:共享对象需要线程安全处理
  3. 外部状态管理复杂:客户端需要维护外部状态
  4. 可能影响执行效率:传递外部状态可能降低性能

使用场景

  1. 大量相似对象:系统中有大量相似对象,且这些对象可以共享部分状态
  2. 内存敏感应用:移动设备、嵌入式系统等内存受限环境
  3. 缓存数据:需要缓存大量重复数据
  4. 游戏开发:大量粒子、树木、NPC等游戏对象
  5. 图形编辑软件:大量图形元素共享样式属性

享元模式 vs 其他模式

模式 区别
享元 vs 单例 享元可以有多个共享实例,单例只有一个实例
享元 vs 原型 享元共享不变部分,原型复制整个对象
享元 vs 组合 享元处理大量细粒度对象,组合处理树形结构
享元 vs 对象池 享元共享不可变部分,对象池复用整个对象

Java中的享元模式应用

  1. String常量池:Java中的字符串常量池就是享元模式
  2. Integer缓存:Integer.valueOf()缓存-128到127的整数
  3. JDBC连接池:数据库连接共享
  4. 线程池:线程对象重用

实际应用技巧

  1. 识别内部状态:找出对象中不变的部分
  2. 外部状态参数化:将变化的部分作为参数传递
  3. 使用工厂管理:确保对象正确共享
  4. 考虑线程安全:共享对象需要同步机制
  5. 权衡利弊:不是所有场景都适合享元模式

享元模式的核心价值在于:通过共享来高效地支持大量细粒度对象,在内存和性能之间找到平衡。当系统中存在大量相似对象时,享元模式可以显著提高性能并减少内存占用。

代理设计模式

通俗解释

想象一下明星和经纪人的关系:

  • 明星(真实对象):负责核心工作(唱歌、演戏)
  • 经纪人(代理对象):负责处理各种杂事(安排行程、过滤粉丝、谈判合同)

当粉丝想见明星时,不能直接联系明星,而是通过经纪人。经纪人会:

  1. 先筛选粉丝的请求(访问控制)
  2. 安排合适的时间(延迟处理)
  3. 记录所有见面记录(日志记录)
  4. 最后才让粉丝见到明星

核心思想为另一个对象提供一个代理或占位符,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用。

关键特点

  1. 控制访问:限制、增强或修改对目标对象的访问
  2. 透明性:客户端不知道自己在使用代理,以为直接和目标对象交互
  3. 延迟初始化:可以推迟创建开销大的对象
  4. 开闭原则:可以在不修改目标对象的情况下增加新功能

代理模式类型

  1. 虚拟代理:延迟创建开销大的对象
  2. 保护代理:控制访问权限
  3. 远程代理:为远程对象提供本地代表
  4. 智能代理:添加额外功能(日志、缓存等)

现实生活例子

  1. VPN代理:访问受限网站的中介
  2. 信用卡:现金的代理,提供额外功能(积分、分期)
  3. 网页缓存:保存网页副本,减少服务器请求
  4. 门禁系统:控制对建筑物的访问
  5. 游戏中的懒加载:只在需要时加载高清纹理

代码示例

示例1:虚拟代理 - 图片加载器(经典例子)

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

// 1. 主题接口:图片
interface Image {
    void display();
    String getFileName();
}

// 2. 真实主题:高分辨率图片(创建成本高)
class HighResolutionImage implements Image {
    private String fileName;
    private int width;
    private int height;
    private byte[] imageData;
    
    public HighResolutionImage(String fileName) {
        this.fileName = fileName;
        loadImageFromDisk(); // 模拟高成本操作
    }
    
    private void loadImageFromDisk() {
        System.out.println("正在从磁盘加载高分辨率图片: " + fileName);
        // 模拟耗时操作
        try {
            Thread.sleep(2000); // 模拟加载时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        // 模拟加载图片数据(这里用随机大小代替)
        this.width = 1920 + (int)(Math.random() * 1000);
        this.height = 1080 + (int)(Math.random() * 1000);
        this.imageData = new byte[width * height * 3]; // 假设每个像素3字节
        
        System.out.println("图片加载完成: " + fileName + 
                          " (" + width + "x" + height + ")");
    }
    
    @Override
    public void display() {
        System.out.println("显示高分辨率图片: " + fileName + 
                          " (" + width + "x" + height + ")");
        // 在实际应用中,这里会调用图形API显示图片
    }
    
    @Override
    public String getFileName() {
        return fileName;
    }
    
    public int getWidth() { return width; }
    public int getHeight() { return height; }
}

// 3. 虚拟代理:图片代理(延迟加载)
class ImageProxy implements Image {
    private String fileName;
    private HighResolutionImage realImage; // 真实图片(延迟加载)
    private boolean isLoaded = false;
    
    public ImageProxy(String fileName) {
        this.fileName = fileName;
        System.out.println("创建图片代理: " + fileName + "(图片尚未加载)");
    }
    
    @Override
    public void display() {
        // 延迟加载:只在需要显示时才加载真实图片
        if (!isLoaded) {
            loadRealImage();
        }
        realImage.display();
    }
    
    @Override
    public String getFileName() {
        return fileName;
    }
    
    private void loadRealImage() {
        if (realImage == null) {
            realImage = new HighResolutionImage(fileName);
            isLoaded = true;
        }
    }
    
    // 代理还可以提供额外功能
    public void preload() {
        System.out.println("预加载图片: " + fileName);
        loadRealImage();
    }
    
    public boolean isImageLoaded() {
        return isLoaded;
    }
    
    public void showThumbnail() {
        System.out.println("显示缩略图: " + fileName + " [低分辨率预览]");
        // 显示低分辨率预览,不需要加载完整图片
    }
}

// 4. 客户端:图片查看器
class ImageViewer {
    private List<Image> images = new ArrayList<>();
    
    public void addImage(Image image) {
        images.add(image);
    }
    
    public void displayAllImages() {
        System.out.println("\n=== 显示所有图片 ===");
        for (Image image : images) {
            image.display();
        }
    }
    
    public void displayImage(int index) {
        if (index >= 0 && index < images.size()) {
            images.get(index).display();
        }
    }
    
    public void showAllThumbnails() {
        System.out.println("\n=== 显示所有缩略图 ===");
        for (Image image : images) {
            if (image instanceof ImageProxy) {
                ((ImageProxy) image).showThumbnail();
            } else {
                System.out.println("显示: " + image.getFileName());
            }
        }
    }
}

// 5. 主程序
public class VirtualProxyDemo {
    public static void main(String[] args) {
        System.out.println("=== 代理模式示例:虚拟代理(图片延迟加载) ===\n");
        
        // 创建图片查看器
        ImageViewer viewer = new ImageViewer();
        
        System.out.println("创建图片对象(使用代理)...");
        
        // 使用代理创建图片对象
        viewer.addImage(new ImageProxy("nature.jpg"));
        viewer.addImage(new ImageProxy("city.jpg"));
        viewer.addImage(new ImageProxy("portrait.jpg"));
        viewer.addImage(new ImageProxy("landscape.jpg"));
        viewer.addImage(new ImageProxy("abstract.jpg"));
        
        System.out.println("\n所有图片代理已创建,但真实图片尚未加载。");
        System.out.println("内存占用小,启动速度快。\n");
        
        // 用户浏览缩略图(不需要加载完整图片)
        viewer.showAllThumbnails();
        
        System.out.println("\n" + "=".repeat(50));
        System.out.println("用户点击查看第一张图片...");
        viewer.displayImage(0); // 只有这张图片会被加载
        
        System.out.println("\n" + "=".repeat(50));
        System.out.println("用户点击查看第三张图片...");
        viewer.displayImage(2); // 只有这张图片会被加载
        
        System.out.println("\n" + "=".repeat(50));
        System.out.println("用户决定查看所有图片...");
        viewer.displayAllImages(); // 加载剩余所有图片
        
        // 性能对比
        System.out.println("\n" + "=".repeat(50));
        System.out.println("=== 性能对比:直接加载 vs 代理延迟加载 ===");
        
        long startTime, endTime;
        
        // 直接加载所有图片
        System.out.println("\n1. 直接加载所有图片(传统方式):");
        startTime = System.currentTimeMillis();
        
        List<HighResolutionImage> directImages = new ArrayList<>();
        directImages.add(new HighResolutionImage("photo1.jpg"));
        directImages.add(new HighResolutionImage("photo2.jpg"));
        directImages.add(new HighResolutionImage("photo3.jpg"));
        directImages.add(new HighResolutionImage("photo4.jpg"));
        directImages.add(new HighResolutionImage("photo5.jpg"));
        
        endTime = System.currentTimeMillis();
        System.out.println("启动时间: " + (endTime - startTime) + "ms");
        System.out.println("内存占用: 高(所有图片已加载)");
        
        // 使用代理延迟加载
        System.out.println("\n2. 使用代理延迟加载:");
        startTime = System.currentTimeMillis();
        
        ImageViewer proxyViewer = new ImageViewer();
        proxyViewer.addImage(new ImageProxy("photo1.jpg"));
        proxyViewer.addImage(new ImageProxy("photo2.jpg"));
        proxyViewer.addImage(new ImageProxy("photo3.jpg"));
        proxyViewer.addImage(new ImageProxy("photo4.jpg"));
        proxyViewer.addImage(new ImageProxy("photo5.jpg"));
        
        endTime = System.currentTimeMillis();
        System.out.println("启动时间: " + (endTime - startTime) + "ms");
        System.out.println("内存占用: 低(只有代理对象)");
        
        // 模拟用户行为:只查看部分图片
        System.out.println("\n模拟用户行为(只查看2张图片):");
        proxyViewer.displayImage(0);
        proxyViewer.displayImage(2);
        System.out.println("实际加载图片数: 2张(节省了3张图片的加载)");
    }
}

示例2:保护代理 - 文档访问控制系统

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

// 1. 主题接口:文档
interface Document {
    void view();
    void edit();
    void delete();
    String getContent();
    String getTitle();
}

// 2. 真实主题:敏感文档
class SensitiveDocument implements Document {
    private String title;
    private String content;
    private String classification; // 机密等级
    
    public SensitiveDocument(String title, String content, String classification) {
        this.title = title;
        this.content = content;
        this.classification = classification;
    }
    
    @Override
    public void view() {
        System.out.println("查看文档: " + title);
        System.out.println("内容: " + content);
    }
    
    @Override
    public void edit() {
        System.out.println("编辑文档: " + title);
        // 实际编辑操作
    }
    
    @Override
    public void delete() {
        System.out.println("删除文档: " + title);
        // 实际删除操作
    }
    
    @Override
    public String getContent() {
        return content;
    }
    
    @Override
    public String getTitle() {
        return title;
    }
    
    public String getClassification() {
        return classification;
    }
}

// 3. 用户类
class User {
    private String username;
    private String role; // 角色:admin, editor, viewer
    private List<String> permissions;
    
    public User(String username, String role) {
        this.username = username;
        this.role = role;
        this.permissions = new ArrayList<>();
        
        // 根据角色分配权限
        switch(role) {
            case "admin":
                permissions.add("view");
                permissions.add("edit");
                permissions.add("delete");
                break;
            case "editor":
                permissions.add("view");
                permissions.add("edit");
                break;
            case "viewer":
                permissions.add("view");
                break;
        }
    }
    
    public boolean hasPermission(String permission) {
        return permissions.contains(permission);
    }
    
    public String getUsername() { return username; }
    public String getRole() { return role; }
}

// 4. 保护代理:文档访问控制代理
class DocumentAccessProxy implements Document {
    private SensitiveDocument realDocument;
    private User user;
    
    public DocumentAccessProxy(SensitiveDocument document, User user) {
        this.realDocument = document;
        this.user = user;
    }
    
    @Override
    public void view() {
        if (checkAccess("view")) {
            System.out.println("用户 '" + user.getUsername() + "' 有查看权限");
            realDocument.view();
        } else {
            System.out.println("访问被拒绝: 用户 '" + user.getUsername() + 
                             "' 没有查看文档 '" + realDocument.getTitle() + "' 的权限");
        }
    }
    
    @Override
    public void edit() {
        if (checkAccess("edit")) {
            System.out.println("用户 '" + user.getUsername() + "' 有编辑权限");
            realDocument.edit();
        } else {
            System.out.println("访问被拒绝: 用户 '" + user.getUsername() + 
                             "' 没有编辑文档 '" + realDocument.getTitle() + "' 的权限");
        }
    }
    
    @Override
    public void delete() {
        if (checkAccess("delete")) {
            System.out.println("用户 '" + user.getUsername() + "' 有删除权限");
            realDocument.delete();
        } else {
            System.out.println("访问被拒绝: 用户 '" + user.getUsername() + 
                             "' 没有删除文档 '" + realDocument.getTitle() + "' 的权限");
        }
    }
    
    @Override
    public String getContent() {
        if (checkAccess("view")) {
            return realDocument.getContent();
        } else {
            return "[访问被拒绝: 您没有查看此文档的权限]";
        }
    }
    
    @Override
    public String getTitle() {
        return realDocument.getTitle();
    }
    
    private boolean checkAccess(String operation) {
        // 检查用户是否有相应权限
        boolean hasPermission = user.hasPermission(operation);
        
        // 可以添加更复杂的访问控制逻辑
        if (operation.equals("view") && realDocument.getClassification().equals("绝密")) {
            // 只有管理员可以查看绝密文档
            return user.getRole().equals("admin");
        }
        
        return hasPermission;
    }
    
    // 代理特有方法:获取访问日志
    public void logAccess(String operation) {
        System.out.println("日志: 用户 '" + user.getUsername() + 
                         "' 尝试 " + operation + " 文档 '" + 
                         realDocument.getTitle() + "' - " + 
                         new Date());
    }
}

// 5. 文档管理系统
class DocumentManagementSystem {
    private Map<String, Document> documents = new HashMap<>();
    
    public void addDocument(String id, Document document) {
        documents.put(id, document);
    }
    
    public Document getDocument(String id, User user) {
        Document doc = documents.get(id);
        if (doc == null) {
            System.out.println("文档不存在: " + id);
            return null;
        }
        
        // 如果是真实文档,返回保护代理
        if (doc instanceof SensitiveDocument) {
            return new DocumentAccessProxy((SensitiveDocument) doc, user);
        }
        
        return doc;
    }
    
    public void listDocuments(User user) {
        System.out.println("\n=== 文档列表(用户: " + user.getUsername() + ") ===");
        for (Map.Entry<String, Document> entry : documents.entrySet()) {
            Document doc = entry.getValue();
            // 通过代理访问标题
            Document proxy = getDocument(entry.getKey(), user);
            System.out.println("ID: " + entry.getKey() + 
                             ", 标题: " + proxy.getTitle());
        }
    }
}

// 6. 主程序
public class ProtectionProxyDemo {
    public static void main(String[] args) {
        System.out.println("=== 代理模式示例:保护代理(文档访问控制) ===\n");
        
        // 创建用户
        User admin = new User("admin_user", "admin");
        User editor = new User("editor_user", "editor");
        User viewer = new User("viewer_user", "viewer");
        
        System.out.println("创建用户:");
        System.out.println("1. " + admin.getUsername() + " (管理员)");
        System.out.println("2. " + editor.getUsername() + " (编辑者)");
        System.out.println("3. " + viewer.getUsername() + " (查看者)\n");
        
        // 创建文档管理系统
        DocumentManagementSystem dms = new DocumentManagementSystem();
        
        // 添加敏感文档
        dms.addDocument("doc001", new SensitiveDocument(
            "公司财务报告", 
            "2023年总收入: 1,000万元,利润: 200万元", 
            "机密"
        ));
        
        dms.addDocument("doc002", new SensitiveDocument(
            "新产品研发计划", 
            "下一代产品将在2024年Q2发布,预算500万元", 
            "秘密"
        ));
        
        dms.addDocument("doc003", new SensitiveDocument(
            "战略合作备忘录", 
            "与ABC公司的合作细节...", 
            "绝密"
        ));
        
        dms.addDocument("doc004", new SensitiveDocument(
            "员工手册", 
            "公司规章制度...", 
            "公开"
        ));
        
        System.out.println("文档系统已初始化,包含4个文档\n");
        
        // 测试不同用户的访问权限
        System.out.println("=".repeat(60));
        System.out.println("测试1: 管理员访问文档");
        System.out.println("=".repeat(60));
        testUserAccess(dms, admin, "doc003");
        
        System.out.println("\n" + "=".repeat(60));
        System.out.println("测试2: 编辑者访问文档");
        System.out.println("=".repeat(60));
        testUserAccess(dms, editor, "doc002");
        
        System.out.println("\n" + "=".repeat(60));
        System.out.println("测试3: 查看者访问文档");
        System.out.println("=".repeat(60));
        testUserAccess(dms, viewer, "doc001");
        
        System.out.println("\n" + "=".repeat(60));
        System.out.println("测试4: 查看者尝试编辑文档");
        System.out.println("=".repeat(60));
        testUserEdit(dms, viewer, "doc004");
        
        System.out.println("\n" + "=".repeat(60));
        System.out.println("测试5: 编辑者尝试删除文档");
        System.out.println("=".repeat(60));
        testUserDelete(dms, editor, "doc001");
        
        System.out.println("\n" + "=".repeat(60));
        System.out.println("文档列表视图(不同用户看到的内容)");
        System.out.println("=".repeat(60));
        
        System.out.println("\n管理员看到的文档列表:");
        dms.listDocuments(admin);
        
        System.out.println("\n查看者看到的文档列表:");
        dms.listDocuments(viewer);
        
        // 显示所有文档内容(通过代理)
        System.out.println("\n" + "=".repeat(60));
        System.out.println("显示所有文档内容(通过代理访问控制)");
        System.out.println("=".repeat(60));
        
        User[] users = {admin, editor, viewer};
        String[] docIds = {"doc001", "doc002", "doc003", "doc004"};
        
        for (User user : users) {
            System.out.println("\n用户: " + user.getUsername() + " (" + user.getRole() + ")");
            for (String docId : docIds) {
                Document doc = dms.getDocument(docId, user);
                if (doc != null) {
                    System.out.println("文档: " + doc.getTitle());
                    System.out.println("内容预览: " + 
                        doc.getContent().substring(0, Math.min(30, doc.getContent().length())) + "...");
                }
            }
        }
    }
    
    private static void testUserAccess(DocumentManagementSystem dms, User user, String docId) {
        Document doc = dms.getDocument(docId, user);
        System.out.println("用户: " + user.getUsername() + 
                         " (" + user.getRole() + ") 尝试查看文档 " + docId);
        if (doc != null) {
            doc.view();
        }
    }
    
    private static void testUserEdit(DocumentManagementSystem dms, User user, String docId) {
        Document doc = dms.getDocument(docId, user);
        System.out.println("用户: " + user.getUsername() + 
                         " (" + user.getRole() + ") 尝试编辑文档 " + docId);
        if (doc != null) {
            doc.edit();
        }
    }
    
    private static void testUserDelete(DocumentManagementSystem dms, User user, String docId) {
        Document doc = dms.getDocument(docId, user);
        System.out.println("用户: " + user.getUsername() + 
                         " (" + user.getRole() + ") 尝试删除文档 " + docId);
        if (doc != null) {
            doc.delete();
        }
    }
}

示例3:智能代理 - 带缓存和日志的数据库查询

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

// 1. 主题接口:数据库查询
interface DatabaseQuery {
    List<Map<String, Object>> executeQuery(String sql);
    String getLastQuery();
    long getLastExecutionTime();
}

// 2. 真实主题:实际数据库查询(开销大)
class RealDatabaseQuery implements DatabaseQuery {
    private String lastQuery;
    private long lastExecutionTime;
    
    @Override
    public List<Map<String, Object>> executeQuery(String sql) {
        System.out.println("执行真实数据库查询: " + sql);
        lastQuery = sql;
        
        long startTime = System.currentTimeMillis();
        
        // 模拟数据库查询(耗时操作)
        try {
            Thread.sleep(1000 + (long)(Math.random() * 2000)); // 1-3秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        // 模拟查询结果
        List<Map<String, Object>> results = new ArrayList<>();
        int rowCount = 5 + (int)(Math.random() * 10); // 5-15行
        
        for (int i = 0; i < rowCount; i++) {
            Map<String, Object> row = new HashMap<>();
            row.put("id", i + 1);
            row.put("name", "用户" + (i + 1));
            row.put("age", 20 + (int)(Math.random() * 30));
            row.put("email", "user" + (i + 1) + "@example.com");
            results.add(row);
        }
        
        lastExecutionTime = System.currentTimeMillis() - startTime;
        System.out.println("查询完成,返回 " + rowCount + " 行数据,耗时 " + lastExecutionTime + "ms");
        
        return results;
    }
    
    @Override
    public String getLastQuery() {
        return lastQuery;
    }
    
    @Override
    public long getLastExecutionTime() {
        return lastExecutionTime;
    }
}

// 3. 智能代理:带缓存、日志和监控的数据库查询代理
class SmartDatabaseQueryProxy implements DatabaseQuery {
    private RealDatabaseQuery realQuery;
    private Map<String, CacheEntry> queryCache;
    private List<QueryLog> queryLogs;
    
    // 缓存条目
    static class CacheEntry {
        List<Map<String, Object>> results;
        long timestamp;
        long ttl; // 生存时间(毫秒)
        
        CacheEntry(List<Map<String, Object>> results, long ttl) {
            this.results = results;
            this.timestamp = System.currentTimeMillis();
            this.ttl = ttl;
        }
        
        boolean isExpired() {
            return System.currentTimeMillis() - timestamp > ttl;
        }
    }
    
    // 查询日志
    static class QueryLog {
        String sql;
        long timestamp;
        long executionTime;
        boolean cached;
        
        QueryLog(String sql, long executionTime, boolean cached) {
            this.sql = sql;
            this.timestamp = System.currentTimeMillis();
            this.executionTime = executionTime;
            this.cached = cached;
        }
    }
    
    public SmartDatabaseQueryProxy() {
        this.queryCache = new HashMap<>();
        this.queryLogs = new ArrayList<>();
    }
    
    @Override
    public List<Map<String, Object>> executeQuery(String sql) {
        long startTime = System.currentTimeMillis();
        List<Map<String, Object>> results;
        boolean fromCache = false;
        
        // 1. 检查缓存
        if (queryCache.containsKey(sql)) {
            CacheEntry entry = queryCache.get(sql);
            if (!entry.isExpired()) {
                System.out.println("从缓存获取查询结果: " + sql);
                results = entry.results;
                fromCache = true;
                
                // 记录缓存命中
                queryLogs.add(new QueryLog(sql, System.currentTimeMillis() - startTime, true));
                
                return results;
            } else {
                System.out.println("缓存已过期: " + sql);
                queryCache.remove(sql);
            }
        }
        
        // 2. 执行真实查询(延迟初始化)
        if (realQuery == null) {
            realQuery = new RealDatabaseQuery();
            System.out.println("初始化真实数据库连接...");
        }
        
        // 3. 执行查询
        results = realQuery.executeQuery(sql);
        
        // 4. 缓存结果(TTL = 30秒)
        queryCache.put(sql, new CacheEntry(results, 30000));
        
        // 5. 记录日志
        long executionTime = System.currentTimeMillis() - startTime;
        queryLogs.add(new QueryLog(sql, executionTime, false));
        
        return results;
    }
    
    @Override
    public String getLastQuery() {
        if (realQuery != null) {
            return realQuery.getLastQuery();
        }
        return "No queries executed yet";
    }
    
    @Override
    public long getLastExecutionTime() {
        if (realQuery != null) {
            return realQuery.getLastExecutionTime();
        }
        return 0;
    }
    
    // 代理特有方法
    public void clearCache() {
        System.out.println("清除所有缓存");
        queryCache.clear();
    }
    
    public void showCacheStats() {
        System.out.println("\n=== 缓存统计 ===");
        System.out.println("缓存条目数: " + queryCache.size());
        
        int expiredCount = 0;
        for (CacheEntry entry : queryCache.values()) {
            if (entry.isExpired()) {
                expiredCount++;
            }
        }
        System.out.println("已过期条目: " + expiredCount);
        
        // 显示缓存内容
        if (!queryCache.isEmpty()) {
            System.out.println("\n缓存内容:");
            for (Map.Entry<String, CacheEntry> entry : queryCache.entrySet()) {
                String sql = entry.getKey();
                CacheEntry cacheEntry = entry.getValue();
                long age = System.currentTimeMillis() - cacheEntry.timestamp;
                System.out.println("SQL: " + sql.substring(0, Math.min(30, sql.length())) + "...");
                System.out.println("  缓存时间: " + age + "ms 前");
                System.out.println("  结果数: " + cacheEntry.results.size());
                System.out.println("  是否过期: " + (cacheEntry.isExpired() ? "是" : "否"));
            }
        }
    }
    
    public void showQueryLogs() {
        System.out.println("\n=== 查询日志 ===");
        System.out.println("总查询次数: " + queryLogs.size());
        
        long totalTime = 0;
        int cacheHits = 0;
        
        for (QueryLog log : queryLogs) {
            totalTime += log.executionTime;
            if (log.cached) {
                cacheHits++;
            }
        }
        
        System.out.println("缓存命中率: " + 
            (queryLogs.size() > 0 ? (cacheHits * 100 / queryLogs.size()) : 0) + "%");
        System.out.println("平均查询时间: " + 
            (queryLogs.size() > 0 ? (totalTime / queryLogs.size()) : 0) + "ms");
        
        // 显示最近日志
        System.out.println("\n最近查询记录:");
        int limit = Math.min(5, queryLogs.size());
        for (int i = Math.max(0, queryLogs.size() - limit); i < queryLogs.size(); i++) {
            QueryLog log = queryLogs.get(i);
            System.out.println("SQL: " + log.sql.substring(0, Math.min(30, log.sql.length())) + "...");
            System.out.println("  时间: " + new Date(log.timestamp));
            System.out.println("  耗时: " + log.executionTime + "ms");
            System.out.println("  来源: " + (log.cached ? "缓存" : "数据库"));
        }
    }
    
    public void setCacheTTL(String sql, long ttl) {
        if (queryCache.containsKey(sql)) {
            queryCache.get(sql).ttl = ttl;
            System.out.println("更新缓存TTL: " + sql + " -> " + ttl + "ms");
        }
    }
}

// 4. 客户端:应用程序
public class SmartProxyDemo {
    public static void main(String[] args) {
        System.out.println("=== 代理模式示例:智能代理(数据库查询缓存) ===\n");
        
        // 创建智能代理
        SmartDatabaseQueryProxy dbProxy = new SmartDatabaseQueryProxy();
        
        System.out.println("初始化应用程序...");
        System.out.println("使用智能数据库查询代理(带缓存功能)\n");
        
        // 模拟应用程序查询
        System.out.println("=".repeat(60));
        System.out.println("模拟用户查询操作");
        System.out.println("=".repeat(60));
        
        // 第一次查询(从数据库)
        System.out.println("\n查询1: 获取所有用户");
        List<Map<String, Object>> results1 = dbProxy.executeQuery(
            "SELECT * FROM users WHERE status = 'active'"
        );
        displayResults(results1);
        
        System.out.println("\n" + "-".repeat(40));
        
        // 第二次相同查询(应该从缓存)
        System.out.println("\n查询2: 再次获取所有用户(应该从缓存)");
        List<Map<String, Object>> results2 = dbProxy.executeQuery(
            "SELECT * FROM users WHERE status = 'active'"
        );
        displayResults(results2);
        
        System.out.println("\n" + "-".repeat(40));
        
        // 第三次不同查询
        System.out.println("\n查询3: 获取年轻用户");
        List<Map<String, Object>> results3 = dbProxy.executeQuery(
            "SELECT * FROM users WHERE age < 30"
        );
        displayResults(results3);
        
        System.out.println("\n" + "-".repeat(40));
        
        // 第四次查询(再次查询年轻用户,应该从缓存)
        System.out.println("\n查询4: 再次获取年轻用户(应该从缓存)");
        List<Map<String, Object>> results4 = dbProxy.executeQuery(
            "SELECT * FROM users WHERE age < 30"
        );
        displayResults(results4);
        
        System.out.println("\n" + "-".repeat(40));
        
        // 第五次查询(新的查询)
        System.out.println("\n查询5: 获取管理员用户");
        List<Map<String, Object>> results5 = dbProxy.executeQuery(
            "SELECT * FROM users WHERE role = 'admin'"
        );
        displayResults(results5);
        
        System.out.println("\n" + "=".repeat(60));
        System.out.println("显示缓存和日志统计");
        System.out.println("=".repeat(60));
        
        // 显示缓存统计
        dbProxy.showCacheStats();
        
        // 显示查询日志
        dbProxy.showQueryLogs();
        
        System.out.println("\n" + "=".repeat(60));
        System.out.println("模拟缓存过期");
        System.out.println("=".repeat(60));
        
        // 模拟等待缓存过期
        System.out.println("\n等待缓存过期(模拟5秒等待)...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        // 修改缓存TTL
        dbProxy.setCacheTTL("SELECT * FROM users WHERE status = 'active'", 5000);
        
        // 再次查询(部分可能已过期)
        System.out.println("\n查询6: 再次获取所有用户(部分可能从缓存,部分从数据库)");
        List<Map<String, Object>> results6 = dbProxy.executeQuery(
            "SELECT * FROM users WHERE status = 'active'"
        );
        displayResults(results6);
        
        System.out.println("\n" + "=".repeat(60));
        System.out.println("清理缓存");
        System.out.println("=".repeat(60));
        
        // 清理缓存
        dbProxy.clearCache();
        dbProxy.showCacheStats();
        
        System.out.println("\n" + "=".repeat(60));
        System.out.println("最终性能对比");
        System.out.println("=".repeat(60));
        
        // 性能对比
        System.out.println("\n对比:使用代理 vs 直接查询");
        
        // 直接查询(无缓存)
        System.out.println("\n1. 直接查询(无缓存):");
        RealDatabaseQuery directQuery = new RealDatabaseQuery();
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < 3; i++) {
            directQuery.executeQuery("SELECT * FROM products WHERE category = 'electronics'");
        }
        
        long directTime = System.currentTimeMillis() - startTime;
        System.out.println("3次查询总耗时: " + directTime + "ms");
        
        // 使用代理(有缓存)
        System.out.println("\n2. 使用智能代理(有缓存):");
        SmartDatabaseQueryProxy proxy = new SmartDatabaseQueryProxy();
        startTime = System.currentTimeMillis();
        
        for (int i = 0; i < 3; i++) {
            proxy.executeQuery("SELECT * FROM products WHERE category = 'electronics'");
        }
        
        long proxyTime = System.currentTimeMillis() - startTime;
        System.out.println("3次查询总耗时: " + proxyTime + "ms");
        
        System.out.println("\n性能提升: " + (directTime - proxyTime) + "ms (" + 
            (directTime > 0 ? (100 - (proxyTime * 100 / directTime)) : 0) + "%)");
    }
    
    private static void displayResults(List<Map<String, Object>> results) {
        if (results == null || results.isEmpty()) {
            System.out.println("无结果");
            return;
        }
        
        System.out.println("返回 " + results.size() + " 行数据:");
        int count = 0;
        for (Map<String, Object> row : results) {
            if (count++ < 3) { // 只显示前3行
                System.out.println("  行" + count + ": " + row);
            }
        }
        if (results.size() > 3) {
            System.out.println("  ... 还有 " + (results.size() - 3) + " 行");
        }
    }
}

代理模式的优缺点

优点:

  1. 控制访问:可以控制客户端对目标对象的访问
  2. 延迟初始化:虚拟代理可以推迟创建开销大的对象
  3. 增强功能:可以在不修改目标对象的情况下添加额外功能
  4. 开闭原则:新功能通过新代理实现,不修改现有代码
  5. 远程访问:远程代理可以隐藏对象在网络中的细节

缺点:

  1. 增加复杂性:引入代理层增加系统复杂性
  2. 性能开销:代理调用可能增加响应时间
  3. 可能过度使用:不是所有情况都需要代理

代理模式 vs 其他模式

模式 区别
代理 vs 装饰器 代理控制访问,装饰器增强功能
代理 vs 适配器 代理不改变接口,适配器改变接口
代理 vs 外观 代理代表单个对象,外观代表整个子系统
代理 vs 桥接 代理控制访问,桥接分离抽象和实现

实际应用场景

  1. Spring AOP:基于代理实现面向切面编程
  2. Hibernate延迟加载:虚拟代理实现关联对象的延迟加载
  3. Java RMI:远程方法调用使用远程代理
  4. Spring Security:保护代理实现方法级安全控制
  5. MyBatis缓存:缓存代理减少数据库访问

代理模式实现方式

  1. 静态代理:手动创建代理类(如上面示例)
  2. 动态代理 :运行时动态创建代理(Java的Proxy类)
  3. CGLIB代理:通过继承实现代理(不需要接口)
java 复制代码
// 动态代理示例
interface Service {
    void serve();
}

class RealService implements Service {
    public void serve() {
        System.out.println("真实服务");
    }
}

class DynamicProxyDemo {
    public static void main(String[] args) {
        Service realService = new RealService();
        
        Service proxy = (Service) java.lang.reflect.Proxy.newProxyInstance(
            Service.class.getClassLoader(),
            new Class[]{Service.class},
            (proxyObj, method, methodArgs) -> {
                System.out.println("动态代理: 调用前");
                Object result = method.invoke(realService, methodArgs);
                System.out.println("动态代理: 调用后");
                return result;
            }
        );
        
        proxy.serve();
    }
}

代理模式的核心价值在于:通过代理对象控制对目标对象的访问,实现访问控制、延迟初始化、缓存、日志等功能,而不需要修改目标对象本身。


愿我都能在各自的领域里不断成长,勇敢追求梦想,同时也保持对世界的好奇与善意!

相关推荐
老蒋每日coding4 小时前
多智能体系统工作流的设计模式与实现策略
设计模式
进击的小头5 小时前
设计模式组合应用:智能硬件控制系统
c语言·设计模式
小码过河.7 小时前
设计模式——迭代器模式
设计模式·迭代器模式
琹箐19 小时前
设计模式——观察者模式
观察者模式·设计模式
小码过河.1 天前
设计模式——责任链模式
设计模式·责任链模式
sg_knight1 天前
抽象工厂模式(Abstract Factory)
java·python·设计模式·抽象工厂模式·开发
技术与健康1 天前
什么是ADSE?逻辑驱动的软件工程新范式
软件工程
短剑重铸之日1 天前
《设计模式》第二篇:单例模式
java·单例模式·设计模式·懒汉式·恶汉式
J_liaty1 天前
23种设计模式一抽象工厂模式‌
设计模式·抽象工厂模式