Java 8中的規範設計模式
關於規範模式specification Pattern,在我以前的一個工作崗位上,我已經利用這種模式成功地設計並實現了電信領域的實時授權的解決方案,現在又一次出現了類似的問題我的團隊要解決這個問題,然後我注意到我的大多數同事都沒有聽說過這種模式。
背景
從本質上講,這次主要目標是通過Socket/">WebSocket通道或REST API向客戶(例如銀行和合作夥伴)推送近實時通知。一目瞭然沒什麼大不了的,但是當我們有相關數量的原始資料流經管道時,事情會變得有趣,根據客戶建立的某些標準,在發出通知之前,應首先匹配這些標準規則。
為了實現這一目標,Java,Kafka,Cloud Foundry,Istio / Envoy等技術齊上陣,以及最重要的是,軟體設計需要協調一致,以便所有的零碎可以很好地融合在一起,提供可擴充套件,安全,可擴充套件和彈性解決方案,我們的客戶無疑可以依賴。
遵循ofollow,noindex" target="_blank">Eric Evans和Martin Fowler的規範檔案 ,規範模式是一種軟體設計模式,可用於封裝定義所需物件狀態的業務規則。這是一種非常強大的方法,可以減少耦合並提高可擴充套件性,以選擇與特定條件匹配的物件子集。這些標準可以使用邏輯運算子組合,然後形成Eric和Martin稱之為複合規範。
因此受這個簡短定義的啟發,現在想象以下場景與標準匹配外匯交易規則:
- sourceCurrency =“EUR”
- sourceCurrency =“EUR” 或 sourceAmount> 500
- sourceCurrency =“EUR” AND sourceAmount BETWEEN 500 和 1000
- (sourceCurrency =“EUR” AND sourceAmount BETWEEN 500 AND1000)或 sourceCurrency =“USD”
上面我們有四個逐漸複雜的有效場景,客戶可以完美地建立,例如,使用方便的QueryBuilderJS外掛 構建一個漂亮的UI 。話雖如此,下面我將僅概述使用Hibernate儲存規範所需的基礎,然後,如何將它們轉換為Java 8謂詞,這樣以時尚的方式從記憶體資料流中過濾物件。
設計Hibernate實體
我將首先丟擲類圖,顯示實體看起來的大圖,然後將介紹重要的事項。(圖太大見原文 )
建立規範 實體及其FieldSpecification ,應該足以涵蓋所有先前描述的場景,而其他場景甚至更復雜。
請注意,規範 實體本身具有子欄位,這將允許巢狀規範使我們能夠實現非常好的標準複雜度。
建立Java謂詞
一旦我們有了儲存規範的基礎,我們就可以構建理解DB模型的元件,然後我們將建立謂詞。
通過使用Java函數語言程式設計作為這種技術的靈魂,我們的生活變得更加容易,只需要幾個簡單的類,我們就可以將動態建立的規範轉換為謂詞,如下所示:
程式碼如下:
<b>public</b> <b>final</b> <b>class</b> Predicates { <b>public</b> <b>static</b> Predicate<FxTransaction> asPredicate(Set<Specification> specifications) { Objects.requireNonNull(specifications); <b>return</b> specifications.stream() .map(Specification::flattened) .map(Predicates::toSpecificationPredicate) .reduce(SpecificationPredicate::combineWith) .orElseThrow(IllegalStateException::<b>new</b>); } <b>private</b> <b>static</b> SpecificationPredicate toSpecificationPredicate(Stream<Specification> specificationStream) { <b>return</b> specificationStream .map(Predicates::toSpecificationPredicate) .reduce(SpecificationPredicate::combineWith) .orElseThrow(IllegalStateException::<b>new</b>); } <b>private</b> <b>static</b> SpecificationPredicate toSpecificationPredicate(Specification specification) { <b>if</b> (spec<b>if</b>ication.getType().equals(Spec<b>if</b>icationType.FIELD)) { <b>return</b> <b>new</b> FieldSpecificationPredicate((FieldSpecification) specification, Optional.empty()); } <b>throw</b> <b>new</b> IllegalStateException(<font>"SpecificationType["</font><font> + specification.getType() + </font><font>"] not supported"</font><font>); } } </font>
可以在DataMatchingTest 類中找到前面提到場景的其他測試。
結論
初看起來,規格檔案可能看起來很複雜,需要花費大量精力來設定所有內容。但是希望你能在這裡看到它實際上非常簡單,基礎設施到位並開始匹配。此外,我希望你能注意到這種技術在記憶體資料匹配中的價值,以及它如何讓你的生活更輕鬆。最後,我應該說給定規範不經常更改,然後我們應該將謂詞儲存在某種記憶體快取中以減少延遲。
完整的示例程式碼可以在這裡找到:specification-pattern