新型領導設計模式—設計可擴展性的業務架構

開閉原則是軟件設計最重要的原則之一,設計具有良好可擴展性的業務架構極其依賴該原則。業務品種在增多,已有業務自身也在發展,需要設計一套統一,靈活,互相獨立的業務架構。筆者在多個項目中,多次使用新型領導設計模式來設計可擴展的業務架構,覺得有必要總結出來,以供大家參考。本來是想把該模式歸爲23種設計模式之一,可能是由於能力有限,沒能成功。

新型領導,把握大局(主流程),做事開明。新型領導設計模式算是筆者一種戲謔的叫法,只希望容易記憶。

最差的做法

曾經在N個項目中看到無數個if else,每一個業務種類一個else if,沒有流程,沒有擴展點,所有的東西塞到一個class裏面。做得好一點的會抽取幾個公用方法出來(這是代碼複用,不是可擴展)。可能有的同學會反駁,“我從來不設計,不會寫這樣的代碼”。不過我不信你沒有看到過,或者被動寫過類似的代碼(歷史包袱)。反正我被動寫過,想哭的節奏…

理想狀態

Manager統領全局,控制流程,在具體的任務處理上,交給(委託)合適的人來做。
流程以及流程上一些通用的處理是封閉的,任務處理是開放的,A業務交由A處理器操作,B業務交由B處理器操作。示意圖如下:

理想狀態的可擴展性示意圖

流程應該交由類似工作流引擎的系統處理,可擴展性的問題可用新型領導模式解決。

新型領導設計模式

新型領導設計模式要解決兩個問題。
一,Manager如何把多個Employee組織起來?Employee用個List存起來即可。如何發現Employee,最好通過Scan(掃描)或者註冊的方式。二,如何把合適的任務交由合適的人處理?把決策權交給Employee,Manager詢問誰能處理?誰先舉手交給誰處理。

好了,新型領導模式的工作方式如下: Manager接到一個任務,逐個詢問手下的員工,誰能處理這個任務,員工針對任務,評估並回饋Manager是或者否。把任務交給回饋YES的員工。
類圖如下:

新型領導模式類圖

一個例子

投資市場上的投資品有很多種,比如債券,股票,房產,基金,信託,保險等等,統稱爲資產,未來可能還需要支持更多的投資品。現在要設計一個資產系統,有一個功能是計算現值。所有資產都有這個功能,可是每一個的計算方式可能有些不一樣。爲簡化起見,假設暫時僅支持債券和股票。

資產的抽象

public interface Asset {
    /**
     * 資產名稱
     */
    String getAssetName();

    /**
     * 是否固定收益
     */
    boolean isFixed();
}

public class BondAsset implements Asset {

    @Override
    public String getAssetName() {
        return "債券";
    }

    @Override
    public boolean isFixed() {
        return true;
    }
}

public class StockAsset implements Asset {

    @Override
    public String getAssetName() {
        return "股票";
    }

    @Override
    public boolean isFixed() {
        return false;
    }
}

處理器抽象

public interface Handler {
    /**
     * 是否能夠處理該種資產
     */
    boolean canHandle(Asset asset);

    /**
     * 計算現值
     */
    BigDecimal calculateValue(Asset asset);
}

public class BondHandler implements Handler {

    @Override
    public boolean canHandle(Asset asset) {
        return asset instanceof BondAsset;
    }

    @Override
    public BigDecimal calculateValue(Asset asset) {
        // 查詢股票數據庫的上一收盤價
        // 省略N多股票邏輯
        return new BigDecimal(100);
    }
}

public class StockHandler implements Handler {

    @Override
    public boolean canHandle(Asset asset) {
        return asset instanceof StockAsset;
    }

    @Override
    public BigDecimal calculateValue(Asset asset) {
        // 查詢債券數據庫的上一收盤日中值
        // 省略N多債券邏輯
        return new BigDecimal(100);
    }
}

統一調用入口

public interface AssetService {
    /**
     * 計算現值
     */
    BigDecimal calculateValue(Asset asset);
}

public class AssetServiceImpl implements AssetService {
    @Autowired
    private List<Handler> handlers;

    @Override
    public BigDecimal calculateValue(Asset asset) {
        Handler handler = getHandler(asset);
        if (handler == null) {
            throw new RuntimeException("暫時不支持該類型的資產, 找不到對應的Handler");
        }

        return handler.calculateValue(asset);
    }

    /*
     * 詢問處理器,誰能處理該類型資產
     */
    private Handler getHandler(Asset asset) {
        if (handlers == null || handlers.isEmpty()) {
            return null;
        }

        for (Handler handler : handlers) {
            if (handler.canHandle(asset)) {
                return handler;
            }
        }

        return null;
    }
}

例子補充說明

  1. AssetServiceImpl裏面的handlers,是通過Spring的@Autowired運行時注入的。其它的方式包括XML配置註冊,API調用註冊,ClassPath掃描等。

  2. Asset和Handler之間的mapping關係,每一次都調用getHandler,如果handlers數量太多,效率可能比較差。應該嘗試HashMap等其它策略。

  3. 理想狀態下,新增資產,只需要實現Asset,實現Handler即可。比如現在需要支持基金,那麼寫FundAsset和FundHandler兩個類,連配置都不需要。完美的體現開閉原則。

後記

新型領導設計模式有Facade和State設計模式的影子,統一的處理入口體現Facade,By上下文來選擇不同的處理方式,體現State。有可擴展性設計需要的時候,不妨用它來驗證一下,把成篇的If Else扔掉吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章