初学 Java 设计模式(十一):实战外观模式 「类型转换器」

一、外观模式介绍

1. 解决的问题

主要解决访问复杂系统的内部子系统的复杂度问题,简化客户端与其子系统的接口。

2. 定义

外观模式是一种结构型设计模式,能为程序库、框架或其他复杂类提供一个简单的接口。

3. 应用场景

  • 需要一个指向复杂子系统的直接接口,且该接口的功能有限,可以使用外观模式。
  • 需要将子系统组织为多层结构,可以使用外观模式。

二、外观模式优缺点

1. 优点

  • 可以让代码独立于复杂子系统。

2. 缺点

  • 外观可能成为与程序中所有类都耦合的上帝对象。

三、外观模式应用实例:类型转换器

1. 实例场景

在我们的工作中,常常会遇到一些类型转换的问题,比如将一个数组转换成字符串等,我们通常会用一些第三方工具类库来实现,比如 Hutool ,这种工具类库其实就是包含外观模式的思想,我们无需关注内部如何实现类型转换,只需根据方法说明传参即可获取我们想要的转换结果。

今天就以类型转换为例,介绍一下如何使用外观模式。

2. 外观模式实现

2.1 工程结构
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
/**
 * 类型转换
 */
public class Convert {
    /**
     * 转换为字符串
     * 如果给定的值为null,或者转换失败,返回null
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return String
     */
    public static String toString(Object value) {
        return StringConverter.convertToStr(value);
    }
}
2.2.2 转换器

字符串转换器

/**
 * 字符串转换器
 */
public class StringConverter {

    /**
     * 数据转为String
     * 转换规则为:
     * 1、字符串类型将被强转
     * 2、数组将被转换为逗号分隔的字符串
     * 3、其它类型将调用默认的toString()方法
     *
     * @param value 数据
     * @return String
     */
    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)) {
            //对于ASCII字符使用缓存加速转换,减少空间创建
            return CharUtil.toString((char)value);
        }
        return value.toString();
    }
}
2.2.3 文本操作

ASCII字符串缓存

/**
 * ASCII字符串缓存
 */
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);
        }
    }

    /**
     * 字符转为字符串
     * 如果为ASCII字符,使用缓存
     *
     * @param c 字符
     * @return 字符串
     */
    public static String toString(char c) {
        return c < ASCII_LENGTH ? CACHE[c] : String.valueOf(c);
    }
}
2.2.4 工具类

数组工具类

/**
 * 数组工具类
 */
public class ArrayUtil {

    /**
     * 对象是否为数组对象
     *
     * @param obj 对象
     * @return 是否为数组对象,如果为空 返回false
     */
    public static boolean isArray(Object obj) {
        if (null == obj) {
            return false;
        }
        return obj.getClass().isArray();
    }

    /**
     * 数组或集合转String
     *
     * @param obj 集合或数组对象
     * @return 数组字符串,与集合转字符串格式相同
     */
    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();
    }
}

字符工具类

/**
 * 字符工具类
 */
public class CharUtil {

    /**
     * 给定对象对应的类是否为字符类,字符类包括:
     * Character.class
     * char.class
     *
     * @param value 被检查的对象
     * @return true表示为字符类
     */
    public static boolean isChar(Object value) {
        // noinspection ConstantConditions
        return value instanceof Character || value.getClass() == char.class;
    }

    /**
     * 字符转为字符串
     * 如果为ASCII字符,使用缓存
     *
     * @param c 字符
     * @return 字符串
     */
    public static String toString(char c) {
        return ASCIIStrCache.toString(c);
    }
}
2.3 测试验证
2.3.1 测试验证类
/**
 * 转换测试类
 */
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 测试结果
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

四、外观模式结构

外观模式-模式结构图

  1. 外观(Facade)提供了一种访问特定子系统功能的便捷方式,其了解如何重定向客户端请求,知晓如何操作一切活动不见。
  2. 创建附加外观(Additional Facade)类可以避免多种不相关的功能污染单一外观,使其变成又一个复杂结构。客户端和其他外观都可使用附加外观。
  3. 复杂子系统(Complex Subsystem)有数十个不同对象构成。如果要用这些对象完成有意义的工作,必须了解子系统的实现细节,比如按照正确顺序初始化对象和为其提供正确格式的数据。

    子系统类不会意识到外观的存在,它们在系统内运作并且相互之间可直接进行交互。

  4. 客户端(Client)使用外观代替对子系统对象的直接调用。

设计模式并不难学,其本身就是多年经验提炼出的开发指导思想,关键在于多加练习,带着使用设计模式的思想去优化代码,就能构建出更合理的代码。

源码地址: https://github.com/yiyufxst/design-pattern-java

参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei/itstack-demo-design
深入设计模式:https://refactoringguru.cn/design-patterns/catalog

你可能感兴趣的:(初学 Java 设计模式(十一):实战外观模式 「类型转换器」)