不少業務代碼,將通用性的技術邏輯與差別性的業務邏輯混雜在一塊兒,這樣的作法致使:java
開發人員經常要花更多的力氣去理解業務代碼,隱形之中增長了不少理解與維護成本。遺憾的是,不少開發人員還並無充分意識到這一點,甚至以爲這無傷大雅。編程
實際上,這個細微的問題,反映的倒是開發人員廣泛缺少設計性的思考。 若是一個開發人員重視設計思惟,他就會發現,這不只僅是編程細節,而是能夠運用設計去掌控的事情。
工具
那麼,如何將通用性的技術邏輯與差別性的業務邏輯相分離呢? 首先,要明白什麼是技術邏輯,什麼是業務邏輯。設計
好比:遍歷一個商品對象列表,取出全部商品的標題列表。code
技術邏輯:一般是一些通用處理,好比遍歷一個對象列表,取出對象中的某個屬性。 我並不關心是什麼對象列表,或對象的什麼屬性。 我只關心遍歷列表及如何取出對象中的某個屬性。 技術邏輯一般是須要對客戶端屏蔽的底層細節。對象
業務邏輯: 一般是差別性的處理,好比商品列表與商品標題(多是JSON中的某個字段)。 我並不關心如何遍歷列表或取出對象屬性的技術細節,我只關心商品及商品標題。業務邏輯一般是領域須要重點關注的領域知識,要清晰地凸顯出來。開發
下面,將以一個示例來講明,如何將將通用性的技術邏輯與差別性的業務邏輯相分離。
get
請看下面這段從主流程裏抽出來的代碼片斷。代碼清單一:io
List<Integer> packIds = orderDeliveryResult.getData().stream() .filter(x->x.getDeliveryState() == 1 || x.getDeliveryState() == 2 ) .map(x->x.getId()).collect(Collectors.toList());
你能一眼看出這段代碼的含義嗎? 哦,看上去是要拿到一個包裹ID 列表,但是 x.getDeliveryState() == 1 || x.getDeliveryState() == 2
是什麼意思呢? 你得去找做者溝通一下了。
class
這段代碼是將技術邏輯和業務邏輯混雜在一塊兒的典型例子。對於業務語義來講,實際上並不關心 stream, filter, map 這種技術細節。下面看看改寫後會是什麼樣:
List<Integer> packIds = getDeliveredPackIds(orderDeliveryResult.getData()); private List<Integer> getDeliveredPackIds(List<OrderDelivery> orderDelivery) { return StreamUtil.filterAndMap(orderExpresses, oe -> isDelivered(oe), OrderDelivery::getId); } private boolean isDelivered(OrderDelivery oe) { return oe.getDeliveryState() == 1 || oe.getDeliveryState() == 2; }
public class StreamUtil { private StreamUtil() {} public static <T,R> List<R> map(List<T> dataList, Function<T,R> getData) { if (CollectionUtils.isEmpty(dataList)) { return new ArrayList(); } return dataList.stream().map(getData).collect(Collectors.toList()); } public static<T,R> List<R> filterAndMap(List<T> dataList, Predicate<? super T> predicate , Function<T,R> getData) { if (CollectionUtils.isEmpty(dataList)) { return new ArrayList(); } return dataList.stream().filter(predicate).map(getData).collect(Collectors.toList()); } }
改寫後:
原來主流程裏的代碼變成了一行: getDeliveredPackIds(orderDeliveryList); 業務語義凸顯出來了: 哦,原來是要拿到已發貨的包裹列表。一目瞭然,在理解主流程時也不須要切換到理解這種細節層面的東西了。
原來的 stream, filter, map 被分離到 StreamUtil 工具類中,後面能夠反覆使用,而且在實現業務邏輯時,不再須要關心如何遍歷列表、過濾條件、拿到返回值列表這種技術細節了。
分離出了領域知識:isDelivered。 這個實際上應該寫到 orderDelivery 類中。這樣 oe -> isDelivered(oe)
寫成更簡潔的形式: OrderDelivery::isDelivered
。
新的實現方式,實現了一箭三雕:
這就是將通用技術邏輯與業務邏輯分離的三大重要益處!
語言特性
Java8 提供了 stream ,因而開發人員在應用系統裏處處留下了 stream 的足跡; 但是你理解其初衷嗎?
stream 的目的是可以以一種聲明式的方式清晰地凸顯意圖,然而,開發人員很快就將意圖扔到一邊,只圖寫法的快感,根本沒有體現出這種語言特性的初衷。 也許 Java8 的提供者,應該多提供一個 StreamUtil 的類,纔會讓開發人員意識到其根本目的所在。
應當深思語言特性提供的根本目的,並加以適當包裝,以便讓客戶端用更清晰、易懂、便利的方式去表達現實流程和規則,而不是貪圖品嚐一下新鮮特性。
設計思惟
爲何說體現了設計思惟呢?
可擴展性設計,本質上是將變化與不變分離。 技術邏輯表明了通用不變的部分,而業務邏輯表明了複雜多變的部分。將通用性的技術邏輯與差別性的業務邏輯相分離,本質上體現了可擴展性設計思惟。雖然微小的編程問題並不足以體現其優點,但確確實實能很好地鍛鍊可擴展性設計思惟。這種思惟能夠經過每一次的「將通用性的技術邏輯與差別性的業務邏輯相分離」的思考和實踐進行強化。
遠方,即在足下。
領域思惟
領域思惟,其實是一個附加效果。當把通用性的技術邏輯提煉出來後,就能更清晰地看到所要表達的內容,即:領域知識和領域規則。這能讓開發人員更容易意識到真正的關注點所在,而不是沉溺於技術和編程細節。
同時,當清晰地看到領域知識和領域規則的時候,就會思考將它放置在合適的位置,好比實體能力或領域服務, 而不是淹沒在流程代碼裏。
將通用性的技術邏輯與差別性的業務邏輯分離,實現了一箭三雕:凸顯業務語義 ;代碼複用;沉澱領域知識。 編程表達的小小差別,不只僅體現出設計思惟,還體現了一個開發人員是否對領域知識有敏銳的感知。這種微小的差別是很難察覺出來的,但能夠日積月累、積微知著,一旦面對大規模業務系統時,就會體現出它的難得之處。