一、外观模式介绍
1. 解决的问题
主要解决访问复杂系统的内部子系统的复杂度问题,简化客户端与其子系统的接口。
2. 定义
外观模式是一种结构型设计模式,能为程序库、框架或其他复杂类提供一个简单的接口。
3. 应用场景
- 需要一个指向复杂子系统的直接接口,且该接口的功能有限,可以使用外观模式。
- 需要将子系统组织为多层结构,可以使用外观模式。
二、外观模式优缺点
1. 优点
2. 缺点
三、外观模式应用实例:类型转换器
1. 实例场景
在我们的工作中,常常会遇到一些类型转换的问题,比如将一个数组转换成字符串等,我们通常会用一些第三方工具类库来实现,比如 Hutool
,这种工具类库其实就是包含外观模式的思想,我们无需关注内部如何实现类型转换,只需根据方法说明传参即可获取我们想要的转换结果。
今天就以类型转换为例,介绍一下如何使用外观模式。
2. 外观模式实现
2.1 工程结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| facade-pattern └─ src ├─ main │ └─ java │ └─ org.design.pattern.facade │ ├─ convert │ │ └─ Convert.java │ ├─ converter │ │ └─ StringConverter.java │ ├─ text │ │ └─ ASCIIStrCache.java │ └─ utils │ ├─ ArrayUtil.java │ └─ CharUtil.java └─ test └─ java └─ org.design.pattern.facade.test └─ ConvertTest.java
|
2.2 代码实现
2.2.1 Facade
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class Convert {
public static String toString(Object value) { return StringConverter.convertToStr(value); } }
|
2.2.2 转换器
字符串转换器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
public class StringConverter {
public static String convertToStr(Object value) { if (null == value) { return null; } if (value instanceof CharSequence) { return value.toString(); } else if (ArrayUtil.isArray(value)) { return ArrayUtil.toString(value); } else if(CharUtil.isChar(value)) { return CharUtil.toString((char)value); } return value.toString(); } }
|
2.2.3 文本操作
ASCII字符串缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
public class ASCIIStrCache { private static final int ASCII_LENGTH = 128;
private static final String[] CACHE = new String[ASCII_LENGTH];
static { for (char c = 0; c < ASCII_LENGTH; c++) { CACHE[c] = String.valueOf(c); } }
public static String toString(char c) { return c < ASCII_LENGTH ? CACHE[c] : String.valueOf(c); } }
|
2.2.4 工具类
数组工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
public class ArrayUtil {
public static boolean isArray(Object obj) { if (null == obj) { return false; } return obj.getClass().isArray(); }
public static String toString(Object obj) { if (null == obj) { return null; }
if (obj instanceof long[]) { return Arrays.toString((long[]) obj); } else if (obj instanceof int[]) { return Arrays.toString((int[]) obj); } else if (obj instanceof short[]) { return Arrays.toString((short[]) obj); } else if (obj instanceof char[]) { return Arrays.toString((char[]) obj); } else if (obj instanceof byte[]) { return Arrays.toString((byte[]) obj); } else if (obj instanceof boolean[]) { return Arrays.toString((boolean[]) obj); } else if (obj instanceof float[]) { return Arrays.toString((float[]) obj); } else if (obj instanceof double[]) { return Arrays.toString((double[]) obj); } else if (ArrayUtil.isArray(obj)) { try { return Arrays.deepToString((Object[]) obj); } catch (Exception e) { } }
return obj.toString(); } }
|
字符工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
public class CharUtil {
public static boolean isChar(Object value) { return value instanceof Character || value.getClass() == char.class; }
public static String toString(char c) { return ASCIIStrCache.toString(c); } }
|
2.3 测试验证
2.3.1 测试验证类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public class ConvertTest {
private final Logger log = LoggerFactory.getLogger(ConvertTest.class);
@Test public void testConvertString() { int number = 1; log.info("int number = 1 convert string : " + Convert.toString(number)); char charString = 'a'; log.info("char charString = 'a' convert string : " + Convert.toString(charString)); long[] array = {1,2,3,4,5}; log.info("long[] array = {1,2,3,4,5} convert string : " + Convert.toString(array)); } }
|
2.3.2 测试结果
1 2 3 4 5
| 14:50:51.514 [main] INFO o.d.pattern.facade.test.ConvertTest - int number = 1 convert string :1 14:50:51.517 [main] INFO o.d.pattern.facade.test.ConvertTest - char charString = 'a' convert string :a 14:50:51.517 [main] INFO o.d.pattern.facade.test.ConvertTest - long[] array = {1,2,3,4,5} convert string :[1, 2, 3, 4, 5]
Process finished with exit code 0
|
四、外观模式结构

外观(Facade)提供了一种访问特定子系统功能的便捷方式,其了解如何重定向客户端请求,知晓如何操作一切活动不见。
创建附加外观(Additional Facade)类可以避免多种不相关的功能污染单一外观,使其变成又一个复杂结构。客户端和其他外观都可使用附加外观。
复杂子系统(Complex Subsystem)有数十个不同对象构成。如果要用这些对象完成有意义的工作,必须了解子系统的实现细节,比如按照正确顺序初始化对象和为其提供正确格式的数据。
子系统类不会意识到外观的存在,它们在系统内运作并且相互之间可直接进行交互。
客户端(Client)使用外观代替对子系统对象的直接调用。
设计模式并不难学,其本身就是多年经验提炼出的开发指导思想,关键在于多加练习,带着使用设计模式的思想去优化代码,就能构建出更合理的代码。
源码地址:https://github.com/yiyufxst/design-pattern-java
参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei/itstack-demo-design
深入设计模式:https://refactoringguru.cn/design-patterns/catalog