本文参考自书籍《JAVA设计模式》
什么是外观模式
外观模式是一种使用频率非常高的结构性设计模式,它通过引入一个外观角色来简化客户端与子系统之间的交互。为复杂的子系统提供一个统一的入口,使子系统和客户端之间耦合度降低。
外观模式:为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
外观模式的结构
外观模式的代码示例
子系统1
package facade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
/**
* 文件读取功能
*
* @author zhao.hualuo
* Create at 2021/12/24
*/
public class FileReader {
private static final Logger LOGGER = LoggerFactory.getLogger(FileReader.class);
/**
* 读取本地文件
*
* @param fileName 文件名全路径
* @return 文件内容
*/
public String read(String fileName) {
StringBuilder stringBuilder = new StringBuilder();
try {
List<String> fileLines = Files.readAllLines(Paths.get(fileName));
for (String fileLine : fileLines) {
stringBuilder.append(fileLine).append("\n");
}
} catch (FileNotFoundException exception) {
LOGGER.error("文件不存在");
} catch (IOException exception) {
LOGGER.error("解析文件失败", exception);
}
LOGGER.info("读取文件,获取明文:{}", stringBuilder);
return stringBuilder.toString();
}
}
子系统2
package facade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* 文件写入功能
*
* @author zhao.hualuo
* Create at 2021/12/27
*/
public class FileWriter {
private static final Logger LOGGER = LoggerFactory.getLogger(FileWriter.class);
/**
* 写入文件
*
* @param fileName 文件名
* @param lines 内容
* @throws IOException 异常
*/
public void writeFile(String fileName, String lines) throws IOException {
LOGGER.info("写入文件:{} {}", fileName, lines);
if (!Files.exists(Paths.get(fileName))) {
Files.createFile(Paths.get(fileName));
}
Files.write(Paths.get(fileName), lines.getBytes(StandardCharsets.UTF_8));
}
}
子系统3
package facade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Optional;
/**
* MD5加密工具类
*
* @author zhao.hualuo
* Create at 2021/12/24
*/
public class Md5EncryptionUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(Md5EncryptionUtil.class);
private static final String KEY_MD5 = "MD5";
public String encrypt(String baseString) throws Exception {
byte[] digest = null;
try {
MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
digest = md5.digest(baseString.getBytes(StandardCharsets.UTF_8));
} catch (NoSuchAlgorithmException e) {
LOGGER.error("未找到该加密算法", e);
}
return Optional.ofNullable(digest)
.map(param -> new BigInteger(1, param).toString(16))
.orElseThrow(() -> new Exception("加密失败"));
}
}
子系统4
package facade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
* 安全散列算法(SHA)
*
* @author zhao.hualuo
* Create at 2021/12/27
*/
public class ShaEncryptionUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(ShaEncryptionUtil.class);
private static final String KEY_SHA = "SHA";
/**
* SHA加密(比MD5更安全)
*
* @param data 需要加密的数据
* @return 加密后
* @throws NoSuchAlgorithmException 异常
*/
public String encryptSha(String data) throws NoSuchAlgorithmException {
MessageDigest instance = MessageDigest.getInstance(KEY_SHA);
instance.update(data.getBytes(StandardCharsets.UTF_8));
return Arrays.toString(instance.digest());
}
}
外观类1
package facade;
/**
* MD5加密外观类
*
* @author zhao.hualuo
* Create at 2021/12/27
*/
public class Md5EncryptFacade extends AbstractEncryptFacade {
private static final String SPLIT = "\\.";
/** MD5加密工具类 */
private final Md5EncryptionUtil md5EncryptionUtil;
/** 文件写入 */
private final FileWriter fileWriter;
/** 文件读取 */
private final FileReader fileReader;
public Md5EncryptFacade() {
fileWriter = new FileWriter();
fileReader = new FileReader();
md5EncryptionUtil = new Md5EncryptionUtil();
}
/**
* MD5文件加密
*
* @param fileName 文件全路径
* @throws Exception 异常
*/
@Override
public void fileEncrypt(String fileName) throws Exception {
String content = fileReader.read(fileName);
String encrypt = md5EncryptionUtil.encrypt(content);
fileWriter.writeFile(convertToResultFileName(fileName), encrypt);
}
private String convertToResultFileName(String fileName) {
String[] split = fileName.split(SPLIT);
split[0] += "-result";
return String.join(".", split);
}
}
外观类2
package facade;
/**
* SHA加密外观类
*
* @author zhao.hualuo
* Create at 2021/12/27
*/
public class ShaEncryptFacade extends AbstractEncryptFacade {
private static final String SPLIT = "\\.";
/** SHA加密工具类 */
private final ShaEncryptionUtil shaEncryptionUtil;
/** 文件写入 */
private final FileWriter fileWriter;
/** 文件读取 */
private final FileReader fileReader;
public ShaEncryptFacade() {
fileWriter = new FileWriter();
fileReader = new FileReader();
shaEncryptionUtil = new ShaEncryptionUtil();
}
/**
* MD5文件加密
*
* @param fileName 文件全路径
* @throws Exception 异常
*/
@Override
public void fileEncrypt(String fileName) throws Exception {
String content = fileReader.read(fileName);
String encrypt = shaEncryptionUtil.encryptSha(content);
fileWriter.writeFile(convertToResultFileName(fileName), encrypt);
}
private String convertToResultFileName(String fileName) {
String[] split = fileName.split(SPLIT);
split[0] += "-result";
return String.join(".", split);
}
}
抽象外观类
package facade;
/**
* 抽象外观类
*
* @author zhao.hualuo
* Create at 2021/12/27
*/
public abstract class AbstractEncryptFacade {
/**
* 文件加密
*
* @param fileName 文件全路径
* @throws Exception 异常
*/
public abstract void fileEncrypt(String fileName) throws Exception;
}
客户端
package facade;
/**
* 客户端
*
* @author zhao.hualuo
* Create at 2021/12/27
*/
public class Client {
public static void main(String[] args) throws Exception {
AbstractEncryptFacade md5Facade = new Md5EncryptFacade();
md5Facade.fileEncrypt("D:\\Documents\\person\\designPattern\\md5Test.txt");
System.out.println("MD5加密完成");
AbstractEncryptFacade shaFacade = new ShaEncryptFacade();
shaFacade.fileEncrypt("D:\\Documents\\person\\designPattern\\shaTest.txt");
System.out.println("SHA加密完成");
}
}
外观模式的优缺点
外观模式优点
- 它对客户端屏蔽了子系统组件,减少了客户端需要处理的子对象数目,并使子系统使用起来更加容易,通过引入外观模式,客户端代码将变的很简单,与之关联的对象也很少。
- 它实现了子系统和客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可。
- 一个子系统的修改对其他子系统无影响,而且子系统内部变化也不会影响到外观对象。
外观模式缺点
- 不能很好的限制客户端直接使用子系统类,如果客户端对访问子系统类做太多的限制就减少了可变性和灵活性。
- 如果设计不当,增加新的子系统可能需要修改外观内的元外卖,违背了开闭原则。