设计模式-外观模式
什么是外观模式?
外观模式是一种结构型设计模式,它为复杂的子系统提供一个简单的接口。外观模式通过创建一个外观类来隐藏一个复杂系统的复杂性,使得客户端只需要与外观类交互,而不需要直接与复杂的子系统交互。
外观模式包含以下角色:
- 外观(Facade):知道哪些子系统类负责处理请求,将客户端的请求代理给适当的子系统对象
- 子系统(Subsystems):实现子系统的功能,处理由Facade对象指派的任务,没有Facade的任何相关信息
- 客户端(Client):通过Facade接口与子系统进行交互
外观模式的优缺点
优点:
- 简化客户端使用:客户端不需要了解子系统的复杂性,只需要与外观类交互
- 降低耦合度:客户端与子系统之间松耦合,子系统的变化不会影响客户端
- 提高灵活性:可以灵活地切换不同的子系统实现
- 更好的分层:有助于建立分层结构,每层之间通过外观接口交互
- 符合迪米特法则:减少了客户端与子系统之间的依赖关系
缺点:
- 可能违反开闭原则:当需要添加新功能时,可能需要修改外观类
- 可能创建上帝对象:外观类可能变得过于庞大和复杂
- 限制了底层功能:客户端无法直接访问子系统的高级功能
什么场景下使用外观模式
- 为复杂的模块或子系统提供简单的接口
- 客户端程序需要与多个子系统进行交互
- 需要将子系统与客户端解耦,提高子系统的独立性和可移植性
- 构建多层结构的系统,利用外观模式定义系统中每层的入口
- 需要为遗留代码提供现代接口
代码举例
这里以经常碰到的操作文件的业务场景为例
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();
}
}
}
浙公网安备 33010602011771号