Java函數式命令模式
????????輕量級跨包調用解耦方案,讓跨包調用就像調用本地接口那樣簡單。適用與具有公共依賴的兩個jar包,但是又不想相互引入對方作為依賴。
????????函數式命令模式,很好地實現了跨包調用解耦的目標,并且通過泛型+JSON動態轉換保證了靈活性。
????????設計決策:
特性 | 實現效果 | 優勢 |
跨包調用 | 通過字符串Key調用,完全消除編譯期依賴 | 模塊完全隔離 |
類型安全 | 泛型+?Class<T>?確保參數類型正確 | 避免運行時類型錯誤 |
輕量簡潔 | 無狀態、無復雜繼承結構,直接函數式調用 | 比傳統命令模式更簡潔 |
JSON兼容 | 天然支持RPC/微服務改造(未來如需) | 輕松改造成RPC調用 |
????????常量命令:
public interface Command { ????String CMD_MENU_SAVE = "menu.save"; } |
????????命令操作系統:
public abstract class OperateSystem { ????private static final Map<String, Function<JSONObject, ?>> COMMAND_MAP = new HashMap<>(); ????public static <T, R> void registerCommand(String command, Class<T> paramType, Function<T, R> receiver) { ????????COMMAND_MAP.put(command, json -> { ????????????T param = json.toJavaObject(paramType); ????????????if (ObjectUtils.isEmpty(param)) { ????????????????throw new CommonException("Parameter parsing failed, expected type: %s", paramType.getName()); ????????????} ????????????String message = BeanUtils.parseEmptyField(param); ????????????if (StringUtils.isNotBlank(message)) { ????????????????throw new CommonException("Parameter validation failed: %s", message); ????????????} ????????????return receiver.apply(param); ????????}); ????} ????public static void unregisterCommand(String command) { ????????COMMAND_MAP.remove(command); ????} ????public static <R> R execute(String command, JSONObject param) { ????????Function<JSONObject, R> function = getJsonFunction(command); ????????return function.apply(param); ????} ????@SuppressWarnings("unchecked") ????private static <R> Function<JSONObject, R> getJsonFunction(String command) { ????????Function<JSONObject, R> function = (Function<JSONObject, R>) COMMAND_MAP.get(command); ????????if (ObjectUtils.isEmpty(function)) { ????????????throw new CommonException("Unregistered command: %s", command); ????????} ????????return function; ????} ????@SuppressWarnings("unchecked") ????public static <R, E> E execute(String command, Class<E> resultType, JSONObject param) { ????????if (ObjectUtils.isEmpty(resultType)) { ????????????throw new CommonException("Command result type can not be null: %s", command); ????????} ????????Function<JSONObject, R> function = getJsonFunction(command); ????????R result = function.apply(param); ????????if (ObjectUtils.isEmpty(result)) { ????????????return null; ????????} ????????if (resultType.isInstance(result)) { ????????????return (E)result; ????????} ????????throw new CommonException("Command [%s] failed to produce the expected result." + ????????????????" Expected type: %s, Actual type: %s", command, ????????????????resultType.getName(), result.getClass().getName()); ????} } |
????????上述兩個代碼放在公共依賴包中,如common.jar。
????????模塊接收者(引入common.jar依賴):
@PostConstruct??// 依賴注入完成后初始化函數 private void registerCommand() { ????OperateSystem.registerCommand(Command.CMD_MENU_SAVE, SaveMenu.class, this::saveMenu); } public Boolean saveMenu(SaveMenu saveMenu) { ????... ... } |
????????模塊調用者(引入common.jar依賴):
public void analyseBuildCache() { ????... ... ????String rootPath = this.engineer.getInterfaceRootPath(); ????JSONObject param = new JSONObject(); ????param.put("title", rootPath); ????param.put("code", StringUtils.absoluteLowerUnderlineLetter(rootPath)); ????param.put("routePath", rootPath); ????String webPackagePath = this.engineer.getWebPackagePath(); ????param.put("componentPath", webPackagePath.substring(6)); ????param.put("sort", 1); ????OperateSystem.execute(Command.CMD_MENU_SAVE, param); } |
????????對比傳統方法:
方案 | 當前實現 | Spring依賴注入 | RPC調用 |
跨包隔離 | ? 完全隔離 | ? 需暴露接口 | ? 隔離 |
類型安全 | ? 運行時校驗 | ? 編譯期檢查 | ? 需額外契約 |
代碼量 | ? 極少 | ?? 中等 | ??? 復雜 |
性能 | ????直接調用 | ??? 直接調用 | ? 網絡開銷 |
適用場景
????????推薦使用:
??????????模塊化架構中的跨包調用;
??????????需要快速開發且避免依賴傳染的場景;
??????????未來可能改為微服務但不想提前引入復雜設計。
????????不推薦使用:
??????????需要編譯期強類型檢查(考慮接口下沉)。
??????????需要支持事務/重試等復雜控制流(考慮Spring事務代理)。
????????命令模式核心:
????????"將請求封裝為對象,從而使你可以參數化其他對象使用不同的請求"
????????????????????????????????????????????????????????????????????????????????----《設計模式:可復用面向對象軟件的基礎》
本文為作者(難拳)原創,轉載請注明出處。