设计模式-外观模式

什么是外观模式?

外观模式是一种结构型设计模式,它为复杂的子系统提供一个简单的接口。外观模式通过创建一个外观类来隐藏一个复杂系统的复杂性,使得客户端只需要与外观类交互,而不需要直接与复杂的子系统交互。
外观模式包含以下角色:

  • 外观(Facade):知道哪些子系统类负责处理请求,将客户端的请求代理给适当的子系统对象
  • 子系统(Subsystems):实现子系统的功能,处理由Facade对象指派的任务,没有Facade的任何相关信息
  • 客户端(Client):通过Facade接口与子系统进行交互

外观模式的优缺点

优点:

  • 简化客户端使用:客户端不需要了解子系统的复杂性,只需要与外观类交互
  • 降低耦合度:客户端与子系统之间松耦合,子系统的变化不会影响客户端
  • 提高灵活性:可以灵活地切换不同的子系统实现
  • 更好的分层:有助于建立分层结构,每层之间通过外观接口交互
  • 符合迪米特法则:减少了客户端与子系统之间的依赖关系

缺点:

  • 可能违反开闭原则:当需要添加新功能时,可能需要修改外观类
  • 可能创建上帝对象:外观类可能变得过于庞大和复杂
  • 限制了底层功能:客户端无法直接访问子系统的高级功能

什么场景下使用外观模式

  1. 为复杂的模块或子系统提供简单的接口
  2. 客户端程序需要与多个子系统进行交互
  3. 需要将子系统与客户端解耦,提高子系统的独立性和可移植性
  4. 构建多层结构的系统,利用外观模式定义系统中每层的入口
  5. 需要为遗留代码提供现代接口

代码举例

这里以经常碰到的操作文件的业务场景为例

import java.io.*;
import java.nio.file.*;
import java.util.zip.*;
import java.security.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

// 子系统1 - 文件读写操作
class FileIOManager {
    public String readFile(String filePath) throws IOException {
        System.out.println("读取文件: " + filePath);
        return Files.readString(Paths.get(filePath), StandardCharsets.UTF_8);
    }
    
    public void writeFile(String filePath, String content) throws IOException {
        System.out.println("写入文件: " + filePath);
        Files.writeString(Paths.get(filePath), content, StandardCharsets.UTF_8);
    }
    
    public void appendToFile(String filePath, String content) throws IOException {
        System.out.println("追加到文件: " + filePath);
        Files.writeString(Paths.get(filePath), content, StandardCharsets.UTF_8, 
                         StandardOpenOption.CREATE, StandardOpenOption.APPEND);
    }
    
    public boolean fileExists(String filePath) {
        return Files.exists(Paths.get(filePath));
    }
    
    public void deleteFile(String filePath) throws IOException {
        System.out.println("删除文件: " + filePath);
        Files.delete(Paths.get(filePath));
    }
    
    public long getFileSize(String filePath) throws IOException {
        return Files.size(Paths.get(filePath));
    }
}

// 子系统2 - 文件压缩解压缩
class CompressionManager {
    public void compressFile(String sourceFile, String zipFile) throws IOException {
        System.out.println("压缩文件: " + sourceFile + " -> " + zipFile);
        try (FileOutputStream fos = new FileOutputStream(zipFile);
             ZipOutputStream zos = new ZipOutputStream(fos)) {
            
            File fileToZip = new File(sourceFile);
            FileInputStream fis = new FileInputStream(fileToZip);
            ZipEntry zipEntry = new ZipEntry(fileToZip.getName());
            zos.putNextEntry(zipEntry);
            
            byte[] bytes = new byte[1024];
            int length;
            while ((length = fis.read(bytes)) >= 0) {
                zos.write(bytes, 0, length);
            }
            fis.close();
        }
    }
    
    public void decompressFile(String zipFile, String destDir) throws IOException {
        System.out.println("解压缩文件: " + zipFile + " -> " + destDir);
        byte[] buffer = new byte[1024];
        try (FileInputStream fis = new FileInputStream(zipFile);
             ZipInputStream zis = new ZipInputStream(fis)) {
            
            ZipEntry zipEntry = zis.getNextEntry();
            while (zipEntry != null) {
                File newFile = new File(destDir, zipEntry.getName());
                if (zipEntry.isDirectory()) {
                    newFile.mkdirs();
                } else {
                    new File(newFile.getParent()).mkdirs();
                    FileOutputStream fos = new FileOutputStream(newFile);
                    int len;
                    while ((len = zis.read(buffer)) > 0) {
                        fos.write(buffer, 0, len);
                    }
                    fos.close();
                }
                zipEntry = zis.getNextEntry();
            }
            zis.closeEntry();
        }
    }
    
    public List<String> listZipContents(String zipFile) throws IOException {
        List<String> entries = new ArrayList<>();
        try (FileInputStream fis = new FileInputStream(zipFile);
             ZipInputStream zis = new ZipInputStream(fis)) {
            
            ZipEntry zipEntry = zis.getNextEntry();
            while (zipEntry != null) {
                entries.add(zipEntry.getName());
                zipEntry = zis.getNextEntry();
            }
        }
        return entries;
    }
}

// 子系统3 - 文件加密解密
class EncryptionManager {
    public void encryptFile(String inputFile, String outputFile, String password) throws Exception {
        System.out.println("加密文件: " + inputFile + " -> " + outputFile);
        
        // 简化的加密实现(实际应用中应使用更安全的加密算法)
        String content = Files.readString(Paths.get(inputFile));
        String encrypted = Base64.getEncoder().encodeToString(
            (content + ":" + password).getBytes(StandardCharsets.UTF_8)
        );
        Files.writeString(Paths.get(outputFile), encrypted);
    }
    
    public void decryptFile(String inputFile, String outputFile, String password) throws Exception {
        System.out.println("解密文件: " + inputFile + " -> " + outputFile);
        
        String encrypted = Files.readString(Paths.get(inputFile));
        String decoded = new String(Base64.getDecoder().decode(encrypted), StandardCharsets.UTF_8);
        String[] parts = decoded.split(":", 2);
        
        if (parts.length < 2 || !parts[1].equals(password)) {
            throw new SecurityException("密码错误或文件损坏");
        }
        
        Files.writeString(Paths.get(outputFile), parts[0]);
    }
    
    public String calculateMD5(String filePath) throws Exception {
        System.out.println("计算文件MD5: " + filePath);
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] data = Files.readAllBytes(Paths.get(filePath));
        byte[] digest = md.digest(data);
        
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
}

// 子系统4 - 文件备份管理
class BackupManager {
    public void createBackup(String sourceFile, String backupDir) throws IOException {
        System.out.println("创建备份: " + sourceFile + " -> " + backupDir);
        
        Path sourcePath = Paths.get(sourceFile);
        String fileName = sourcePath.getFileName().toString();
        String timestamp = String.valueOf(System.currentTimeMillis());
        String backupFileName = fileName + ".backup." + timestamp;
        
        Path backupPath = Paths.get(backupDir, backupFileName);
        Files.copy(sourcePath, backupPath, StandardCopyOption.REPLACE_EXISTING);
    }
    
    public void restoreBackup(String backupFile, String restorePath) throws IOException {
        System.out.println("恢复备份: " + backupFile + " -> " + restorePath);
        Files.copy(Paths.get(backupFile), Paths.get(restorePath), StandardCopyOption.REPLACE_EXISTING);
    }
    
    public List<String> listBackups(String backupDir) throws IOException {
        List<String> backups = new ArrayList<>();
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(backupDir))) {
            for (Path entry : stream) {
                if (entry.toString().contains(".backup.")) {
                    backups.add(entry.toString());
                }
            }
        }
        return backups;
    }
}

// 子系统5 - 文件格式转换
class FormatConverter {
    public void convertTextFile(String inputFile, String outputFile, String targetEncoding) throws IOException {
        System.out.println("转换文本文件编码: " + inputFile + " -> " + outputFile + " (" + targetEncoding + ")");
        
        // 读取原文件
        String content = Files.readString(Paths.get(inputFile), StandardCharsets.UTF_8);
        // 写入目标文件(简化处理,实际需要处理编码转换)
        Files.writeString(Paths.get(outputFile), content, StandardCharsets.UTF_8);
    }
    
    public void convertToUpperCase(String inputFile, String outputFile) throws IOException {
        System.out.println("转换为大写: " + inputFile + " -> " + outputFile);
        
        String content = Files.readString(Paths.get(inputFile), StandardCharsets.UTF_8);
        Files.writeString(Paths.get(outputFile), content.toUpperCase(), StandardCharsets.UTF_8);
    }
    
    public void removeBlankLines(String inputFile, String outputFile) throws IOException {
        System.out.println("移除空行: " + inputFile + " -> " + outputFile);
        
        List<String> lines = Files.readAllLines(Paths.get(inputFile), StandardCharsets.UTF_8);
        List<String> nonEmptyLines = new ArrayList<>();
        
        for (String line : lines) {
            if (!line.trim().isEmpty()) {
                nonEmptyLines.add(line);
            }
        }
        
        Files.write(Paths.get(outputFile), nonEmptyLines, StandardCharsets.UTF_8);
    }
}

// 外观类 - 文件处理外观
class FileProcessingFacade {
    private FileIOManager fileIOManager;
    private CompressionManager compressionManager;
    private EncryptionManager encryptionManager;
    private BackupManager backupManager;
    private FormatConverter formatConverter;
    
    public FileProcessingFacade() {
        this.fileIOManager = new FileIOManager();
        this.compressionManager = new CompressionManager();
        this.encryptionManager = new EncryptionManager();
        this.backupManager = new BackupManager();
        this.formatConverter = new FormatConverter();
    }
    
    // 简化的安全文件保存
    public boolean saveSecureFile(String filePath, String content, String password) {
        try {
            System.out.println("=== 安全保存文件 ===");
            
            // 1. 创建备份(如果文件已存在)
            if (fileIOManager.fileExists(filePath)) {
                backupManager.createBackup(filePath, System.getProperty("java.io.tmpdir"));
            }
            
            // 2. 保存临时文件
            String tempFile = filePath + ".tmp";
            fileIOManager.writeFile(tempFile, content);
            
            // 3. 加密文件
            encryptionManager.encryptFile(tempFile, filePath, password);
            
            // 4. 删除临时文件
            fileIOManager.deleteFile(tempFile);
            
            System.out.println("文件安全保存成功: " + filePath);
            return true;
            
        } catch (Exception e) {
            System.err.println("安全保存文件失败: " + e.getMessage());
            return false;
        }
    }
    
    // 简化的安全文件读取
    public String readSecureFile(String filePath, String password) {
        try {
            System.out.println("=== 安全读取文件 ===");
            
            // 1. 解密到临时文件
            String tempFile = filePath + ".decrypted";
            encryptionManager.decryptFile(filePath, tempFile, password);
            
            // 2. 读取内容
            String content = fileIOManager.readFile(tempFile);
            
            // 3. 删除临时文件
            fileIOManager.deleteFile(tempFile);
            
            System.out.println("文件安全读取成功: " + filePath);
            return content;
            
        } catch (Exception e) {
            System.err.println("安全读取文件失败: " + e.getMessage());
            return null;
        }
    }
    
    // 简化的文件打包和压缩
    public boolean createSecureArchive(String[] files, String archiveName, String password) {
        try {
            System.out.println("=== 创建安全压缩包 ===");
            
            // 1. 创建临时目录
            String tempDir = System.getProperty("java.io.tmpdir") + "/archive_temp_" + System.currentTimeMillis();
            new File(tempDir).mkdirs();
            
            // 2. 复制文件到临时目录
            for (String file : files) {
                Path source = Paths.get(file);
                Path target = Paths.get(tempDir, source.getFileName().toString());
                Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
            }
            
            // 3. 压缩临时目录
            String zipFile = archiveName + ".zip";
            // 这里简化处理,实际应该压缩整个目录
            compressionManager.compressFile(files[0], zipFile);
            
            // 4. 加密压缩文件
            if (password != null && !password.isEmpty()) {
                String encryptedZip = archiveName + ".encrypted.zip";
                encryptionManager.encryptFile(zipFile, encryptedZip, password);
                fileIOManager.deleteFile(zipFile);
            }
            
            // 5. 清理临时目录
            deleteDirectory(new File(tempDir));
            
            System.out.println("安全压缩包创建成功: " + archiveName);
            return true;
            
        } catch (Exception e) {
            System.err.println("创建安全压缩包失败: " + e.getMessage());
            return false;
        }
    }
    
    // 简化的文件处理流水线
    public boolean processTextFile(String inputFile, String outputFile, ProcessingOptions options) {
        try {
            System.out.println("=== 处理文本文件 ===");
            
            String currentFile = inputFile;
            String tempFile = inputFile + ".temp";
            
            // 1. 创建备份
            if (options.createBackup) {
                backupManager.createBackup(inputFile, System.getProperty("java.io.tmpdir"));
            }
            
            // 2. 格式转换
            if (options.convertToUpperCase) {
                formatConverter.convertToUpperCase(currentFile, tempFile);
                fileIOManager.deleteFile(currentFile);
                currentFile = tempFile;
                tempFile = tempFile + ".temp2";
            }
            
            // 3. 移除空行
            if (options.removeBlankLines) {
                formatConverter.removeBlankLines(currentFile, tempFile);
                fileIOManager.deleteFile(currentFile);
                currentFile = tempFile;
                tempFile = tempFile + ".temp3";
            }
            
            // 4. 压缩(如果需要)
            if (options.compress) {
                String compressedFile = outputFile + ".zip";
                compressionManager.compressFile(currentFile, compressedFile);
                fileIOManager.deleteFile(currentFile);
                currentFile = compressedFile;
            } else {
                // 重命名为目标文件
                Files.move(Paths.get(currentFile), Paths.get(outputFile), 
                          StandardCopyOption.REPLACE_EXISTING);
            }
            
            System.out.println("文本文件处理成功");
            return true;
            
        } catch (Exception e) {
            System.err.println("处理文本文件失败: " + e.getMessage());
            return false;
        }
    }
    
    // 获取文件信息
    public FileInfo getFileInfo(String filePath) {
        try {
            FileInfo info = new FileInfo();
            info.setPath(filePath);
            info.setSize(fileIOManager.getFileSize(filePath));
            info.setMd5(encryptionManager.calculateMD5(filePath));
            info.setExists(fileIOManager.fileExists(filePath));
            return info;
        } catch (Exception e) {
            System.err.println("获取文件信息失败: " + e.getMessage());
            return null;
        }
    }
    
    // 辅助方法:删除目录
    private void deleteDirectory(File directory) {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteDirectory(file);
                    } else {
                        file.delete();
                    }
                }
            }
            directory.delete();
        }
    }
}

// 配置类
class ProcessingOptions {
    public boolean createBackup = false;
    public boolean convertToUpperCase = false;
    public boolean removeBlankLines = false;
    public boolean compress = false;
    
    public ProcessingOptions setCreateBackup(boolean createBackup) {
        this.createBackup = createBackup;
        return this;
    }
    
    public ProcessingOptions setConvertToUpperCase(boolean convertToUpperCase) {
        this.convertToUpperCase = convertToUpperCase;
        return this;
    }
    
    public ProcessingOptions setRemoveBlankLines(boolean removeBlankLines) {
        this.removeBlankLines = removeBlankLines;
        return this;
    }
    
    public ProcessingOptions setCompress(boolean compress) {
        this.compress = compress;
        return this;
    }
}

// 文件信息类
class FileInfo {
    private String path;
    private long size;
    private String md5;
    private boolean exists;
    
    // getters and setters
    public String getPath() { return path; }
    public void setPath(String path) { this.path = path; }
    
    public long getSize() { return size; }
    public void setSize(long size) { this.size = size; }
    
    public String getMd5() { return md5; }
    public void setMd5(String md5) { this.md5 = md5; }
    
    public boolean isExists() { return exists; }
    public void setExists(boolean exists) { this.exists = exists; }
    
    @Override
    public String toString() {
        return "FileInfo{" +
                "path='" + path + '\'' +
                ", size=" + size +
                ", md5='" + md5 + '\'' +
                ", exists=" + exists +
                '}';
    }
}

// 客户端使用示例
public class FileProcessingDemo {
    public static void main(String[] args) {
        FileProcessingFacade fileProcessor = new FileProcessingFacade();
        
        try {
            // 创建测试文件
            String testFile = "test.txt";
            String content = "Hello World!\n\nThis is a test file.\n\nWith some blank lines.\n";
            Files.writeString(Paths.get(testFile), content);
            
            System.out.println("=== 安全文件操作演示 ===");
            // 安全保存文件
            boolean saveResult = fileProcessor.saveSecureFile(testFile, content, "mypassword");
            System.out.println("安全保存结果: " + saveResult);
            
            // 安全读取文件
            String readContent = fileProcessor.readSecureFile(testFile, "mypassword");
            System.out.println("读取内容: " + readContent);
            
            System.out.println("\n=== 文件处理流水线演示 ===");
            // 文件处理流水线
            ProcessingOptions options = new ProcessingOptions()
                .setCreateBackup(true)
                .setConvertToUpperCase(true)
                .setRemoveBlankLines(true);
            
            boolean processResult = fileProcessor.processTextFile(testFile, "processed.txt", options);
            System.out.println("处理结果: " + processResult);
            
            System.out.println("\n=== 文件信息查询演示 ===");
            // 获取文件信息
            FileInfo fileInfo = fileProcessor.getFileInfo(testFile);
            System.out.println("文件信息: " + fileInfo);
            
            System.out.println("\n=== 压缩包创建演示 ===");
            // 创建压缩包
            String[] filesToArchive = {testFile, "processed.txt"};
            boolean archiveResult = fileProcessor.createSecureArchive(filesToArchive, "myarchive", "archivepass");
            System.out.println("压缩包创建结果: " + archiveResult);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

posted @ 2025-09-12 12:22  MaC-Matthew  阅读(12)  评论(0)    收藏  举报