表驅動法, 實戰中學會和 if...else 及 switch 之間進行取捨

1. 概念

表驅動法就是一種編程模式,從表裏面查找信息而不使用邏輯語句。事實上,凡是能通過邏輯語句來選擇的事物,都可以通過查表來選擇。對簡單的情況而言,使用邏輯語句更爲容易和直白。但隨着邏輯鏈的越來越複雜,查表法也就愈發顯得更具吸引力。 - 引用自《代碼大全》

所謂表驅動法(Table-Driven Approach), 又稱之爲表驅動、表驅動方法。 簡單講是指用查表的方式獲取值。

 

2. 常用查表方式

  • 直接訪問(直接訪問表).
  • 索引訪問(索引訪問表)
  • 分段訪問(階梯訪問表)

 

3. 實戰PK

場景描述: 根據壓縮包的後綴名來判斷具體調用哪個方法來進行解壓

3.1 解壓工具類

public class AIUtil {
    private AIUtil() {}
	
    // 解壓 zip 包
    public static List<String> unzip(String srcPath) {
        System.out.println(format("進入 unzip 方法. srcPath={0}.", srcPath));
        // 具體解壓過程省略...
        return Lists.newArrayList(srcPath + File.separator + "/desc");
    }

    // 解壓 tar 包
    public static List<String> untar(String srcPath, int limit) {
        System.out.println(format("進入 untar 方法. srcPath={0}, limit={1}.", srcPath, limit));
        // 具體解壓過程省略...
        return Lists.newArrayList(srcPath + File.separator + "/desc");
    }

    // 解壓 rar 包
    public static List<String> unrar(String srcPath, boolean force) {
        System.out.println(format("進入 unrar 方法. srcPath={0}, force={1}.", srcPath, force));
        // 具體解壓過程省略...
        return Lists.newArrayList(srcPath + File.separator + "/desc");
    }

	// 其他壓縮包格式解壓函數方法...
}

 

3.2 常規條件判斷實現方式

使用常規的 if…else 條件語句進行邏輯判斷

3.2.1 核心工廠方法

private static void logicJudge(String suffix, String srcPath, int limit, boolean force) {
    List<String> images = null;
    if (".zip".equals(suffix)) {
        images = AIUtil.unzip(srcPath);
    } else if (".tar".equals(suffix)) {
        images = AIUtil.untar(srcPath, limit);
    } else if (".rar".equals(suffix)) {
        images = AIUtil.unrar(srcPath, force);
    }
    // 需求迭代, 現在需要支持其他格式的壓縮包解壓, 則此處需要新增 if...else 邏輯
    
    System.out.println(MessageFormat.format("方法 {0} 返回值爲={1}", suffix, images));
}

3.2.2 測試用例

logicJudge(".zip", "/data/zip", -1, true);
System.out.println();

logicJudge(".tar", "/data/tar", 10, true);
System.out.println();

logicJudge(".rar", "/data/rar", -1, false);
System.out.println();

3.2.3 運行結果

進入 unzip 方法. srcPath=/data/zip.
方法 .zip 返回值爲=[/data/zip\/desc]

進入 untar 方法. srcPath=/data/tar, limit=10.
方法 .tar 返回值爲=[/data/tar\/desc]

進入 unrar 方法. srcPath=/data/rar, force=false.
方法 .rar 返回值爲=[/data/rar\/desc]

 

3.3 枚舉訪問表方式

3.3.1 核心方法

@SuppressWarnings("unchecked")
private static void tableDrive(String suffix, List<Object> params) throws Exception {
    MethodEnum method = MethodEnum.suffix2Method(suffix);
    List<String> images = (List<String>) AIUtil.class.getMethod(method.methodName(), method.parameterTypes()).invoke(null, params.toArray());
    System.out.println(format("方法 {0} 返回值爲={1}", method.methodName(), images));
}

3.3.2 核心枚舉類表

public enum MethodEnum {
    /** zip 包 */
    UNZIP(".zip", "unzip", String.class),
    /** tar 包 */
    UNTAR(".tar", "untar", String.class, int.class),
    /** rar 包 */
    UNRAR(".rar", "unrar", String.class, boolean.class)
	
	// 如果需要支持其他格式的壓縮包解壓, 只需要在此處添加對應的枚舉元素即可
	;

    // 文件後綴名
    private String suffixName;
    // 執行的解壓方法名
    @Getter
    private String methodName;
    // 執行方法的參數
    @Getter
    private Class[] parameterTypes;

    MethodEnum(String suffixName, String methodName, Class... parameterTypes) {
        this.suffixName = suffixName;
        this.methodName = methodName;
        this.parameterTypes = parameterTypes;
    }

    public static MethodEnum suffix2Method(String suffixName) {
        for (MethodEnum method : MethodEnum.values()) {
            if (method.suffixName.equals(suffixName)) {
                return method;
            }
        }
        throw new IllegalArgumentException(suffixName);
    }
}

特別說明: parameterTypes 填寫順序需要和具體解壓方法的入參保持一致

3.3.3 測試用例

tableDrive(".zip", Lists.newArrayList("/data/zip"));
System.out.println();

tableDrive(".tar", Lists.newArrayList("/data/tar", 10));
System.out.println();

tableDrive(".rar", Lists.newArrayList("/data/rar", false));
System.out.println();

3.3.4 運行結果

進入 unzip 方法. srcPath=/data/zip.
方法 unzip 返回值爲=[/data/zip\/desc]

進入 untar 方法. srcPath=/data/tar, limit=10.
方法 untar 返回值爲=[/data/tar\/desc]

進入 unrar 方法. srcPath=/data/rar, force=false.
方法 unrar 返回值爲=[/data/rar\/desc]

 

3.4 枚舉訪問表方式(優化版)

3.4.1 核心方法

private static void tableDrive(String suffix, List<Object> params) throws Exception {
	List<String> images = (List<String>) METHODS_MAP.get(suffix).exeMethod().invoke(null, params.toArray());
    System.out.println(format("方法 {0} 返回值爲={1}", suffix, images));
}

3.4.2 方法接口類

反射生成具體的解壓執行方法

public interface IMethod {
    // 反射生成解壓具體執行方法
    Method exeMethod() throws Exception;
}

3.4.3 核心枚舉類表

public enum MethodEnum implements IMethod {
    /** zip 包 */
    UNZIP(".zip") {
        @Override
        public Method exeMethod() throws Exception {
            return UNZIP_TOOP_CLASS.getMethod("unzip", String.class);
        }
    },
    /** tar 包 */
    UNTAR(".tar") {
        @Override
        public Method exeMethod() throws Exception {
            return UNZIP_TOOP_CLASS.getMethod("untar", String.class, int.class);
        }
    },
    /** rar 包 */
    UNRAR(".rar") {
        @Override
        public Method exeMethod() throws Exception {
            return UNZIP_TOOP_CLASS.getMethod("unrar", String.class, boolean.class);
        }
    }
    ;

    // 壓縮包解壓工具類類名
    private static final Class<?> UNZIP_TOOP_CLASS = AIUtil.class;
    // 具體解壓方法名和對應枚舉映射集
    public static final Map<String, IMethod> METHODS_MAP = Maps.newHashMap();
    static {
        for (MethodEnum method : MethodEnum.values()) {
            METHODS_MAP.put(method.suffixName, method);
        }
    }

    // 文件後綴名
    private String suffixName;

    MethodEnum(String suffixName) {
        this.suffixName = suffixName;
    }
}

 

3.5 枚舉訪問表方式(升級版)

3.5.1 核心方法

private static void tableDrive(String suffix, List<Object> params) throws Exception {
	List<String> images = (List<String>) METHODS_MAP.get(suffix).exeMethod().invoke(null, params.toArray());
    System.out.println(format("方法 {0} 返回值爲={1}", suffix, images));
}

3.5.2 方法接口類

反射生成具體的解壓執行方法

public interface IMethod {
    // 反射生成解壓具體執行方法
    Method exeMethod() throws Exception;
}

3.5.3 核心枚舉類表

public enum MethodEnum implements IMethod {
    /** zip 包 */
    UNZIP(".zip") {
        @Override
        public Method exeMethod() throws Exception {
            return UNZIP_METHOD;
        }
    },
    /** tar 包 */
    UNTAR(".tar") {
        @Override
        public Method exeMethod() throws Exception {
            return UNTAR_METHOD;
        }
    },
    /** rar 包 */
    UNRAR(".rar") {
        @Override
        public Method exeMethod() throws Exception {
            return UNRAR_METHOD;
        }
    }
    ;

    // 具體解壓方法名和對應枚舉映射集
    public static final Map<String, IMethod> METHODS_MAP = Maps.newHashMap();
    private static Method UNZIP_METHOD, UNTAR_METHOD, UNRAR_METHOD;
    static {
        // 壓縮包解壓工具類類名
        final Class<?> unzipToolClz = AIUtil.class;
        try {
            // 把耗時的反射代碼挪到類初始化的時候執行
            UNZIP_METHOD = unzipToolClz.getMethod("unzip", String.class);
            UNTAR_METHOD = unzipToolClz.getMethod("untar", String.class, int.class);
            UNRAR_METHOD = unzipToolClz.getMethod("unrar", String.class, boolean.class);
            // 若要支持其他格式的壓縮包, 此處也需反射獲取對應的解壓方法

            for (MethodEnum method : MethodEnum.values()) {
                METHODS_MAP.put(method.suffixName, method);
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    // 文件後綴名
    private String suffixName;

    MethodEnum(String suffixName) {
        this.suffixName = suffixName;
    }
}

 

3.6 哈希索引訪問表方式(最終版)-推薦

3.6.1 核心哈希索引表

// 具體解壓方法名和對應枚舉映射集
public static final Map<String, Method> METHODS_MAP = Maps.newHashMap();
static {
    // 壓縮包解壓工具類類名
    final Class<?> unzipToolClz = AIUtil.class;
    try {
        // 把耗時的反射代碼挪到類初始化的時候執行
        METHODS_MAP.put(".zip", unzipToolClz.getMethod("unzip", String.class));
        METHODS_MAP.put(".tar", unzipToolClz.getMethod("untar", String.class, int.class));
        METHODS_MAP.put(".rar", unzipToolClz.getMethod("unrar", String.class, boolean.class));
        // 若要支持其他格式的壓縮包, 此處也需反射獲取對應的解壓方法
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
}

3.6.2 核心方法

private static void tableDrive(String suffix, List<Object> params) throws Exception {
 	List<String> images = (List<String>) METHODS_MAP.get(suffix).invoke(null, params.toArray());
    System.out.println(format("方法 {0} 返回值爲={1}", suffix, images));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章