带有谓词的Java中的函数样式-第2部分
生活随笔
收集整理的這篇文章主要介紹了
带有谓词的Java中的函数样式-第2部分
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在本文的第一部分中,我們介紹了謂詞,這些謂詞通過具有返回true或false的單個方法的簡單接口,為Java等面向對象的語言帶來了函數式編程的某些好處。 在第二部分和最后一部分中,我們將介紹一些更高級的概念,以使您的謂詞發揮最大作用。 測試中 謂詞發光的一種明顯情況是測試。 每當您需要測試將遍歷數據結構和某些條件邏輯混合在一起的方法時,通過使用謂詞,您可以隔離地測試每一半,首先遍歷數據結構,然后遍歷條件邏輯。 第一步,您只需將始終為true或始終為false謂詞傳遞給該方法即可擺脫條件邏輯,而只專注于對數據結構的正確處理: // check with the always-true predicate
final Iterable<PurchaseOrder> all = orders.selectOrders(Predicates.<PurchaseOrder> alwaysTrue());
assertEquals(2, Iterables.size(all));// check with the always-false predicate
assertTrue(Iterables.isEmpty(orders.selectOrders(Predicates.<PurchaseOrder> alwaysFalse()))); 在第二步中,您只需分別測試每個可能的謂詞。 final CustomerPredicate isForCustomer1 = new CustomerPredicate(CUSTOMER_1);
assertTrue(isForCustomer1.apply(ORDER_1)); // ORDER_1 is for CUSTOMER_1
assertFalse(isForCustomer1.apply(ORDER_2)); // ORDER_2 is for CUSTOMER_2 這個例子很簡單,但是您可以理解。 為了測試更復雜的邏輯,如果測試功能的每半部分還不夠,則可以創建模擬謂詞,例如,一次返回true的謂詞,然后始終返回false。 由于嚴格分離關注點,因此強制這樣的謂詞可以大大簡化您的測試設置。 謂詞對于測試非常有用,以至于如果您傾向于做一些TDD,我的意思是說,如果您可以通過測試的方式影響您的設計方式,那么一旦您知道謂詞,它們肯定會找到進入您設計的方式。 向團隊解釋 在我從事的項目中,團隊最初并不熟悉謂詞。 但是,這個概念很簡單,也很有趣,每個人都可以快速上手。 實際上,令我驚訝的是,謂詞的概念如何從我編寫的代碼自然傳播到我的同事的代碼中,而沒有太多的福音。 我想謂詞的好處不言而喻。 從諸如Apache或Google之類的知名公司那里獲得成熟的API,也有助于說服它是嚴肅的東西。 現在有了功能性編程炒作,它應該更容易出售! 簡單優化 通常的優化是使謂詞盡可能不變和無狀態 ,以使它們的共享無需考慮線程。 這樣就可以在整個過程中使用一個實例(作為一個實例,例如作為靜態最終常量)。 如果需要,可以在運行時緩存在編譯時無法枚舉的最常用謂詞。 與往常一樣,僅當您的探查器報告確實需要它時,才執行此操作。 可能的話,謂詞對象可以在其構造函數中(自然是線程安全的)或惰性地預先計算其評估所涉及的一些計算。 謂詞應無副作用 ,即“只讀”:謂詞的執行不應引起系統狀態的任何可觀察到的變化。 某些謂詞必須具有某種內部狀態,例如用于分頁的基于計數器的謂詞,但它們仍不得更改其所應用系統中的任何狀態。 在內部狀態下,它們也無法共享,但是,如果它們支持每次連續使用之間的重置,則可以在其線程內重用它們。 細粒度的接口:謂詞的受眾更大 在大型應用程序中,您會發現自己為完全不同的類型編寫了非常相似的謂詞,但它們具有與客戶相關的共同屬性。 例如,在管理頁面中,您可能希望按客戶過濾日志; 在CRM頁面中,您要按客戶過濾投訴。 對于每個此類X,您都需要另一個CustomerXPredicate來按客戶對其進行過濾。 但是由于每個X都以某種方式與客戶相關,所以我們可以使用一種方法將其(在Eclipse中提取接口)分解為一個CustomerSpecific接口: public interface CustomerSpecific {Customer getCustomer();
} 這個細粒度的界面讓我想起了某些語言的特質 ,但它沒有可重用的實現。 它也可以看作是在靜態類型語言中引入動態類型的一種方法,因為它可以使用getCustomer ()方法以不同的方式調用任何對象。 當然,我們的類PurchaseOrder現在實現了此接口。 一旦有了CustomerSpecific接口,就可以在其上定義謂詞,而不是像以前那樣在每個特定類型上定義謂詞。 這有助于在整個大型項目中利用少數謂詞。 在這種情況下,謂詞CustomerPredicate與它運行的CustomerSpecific接口位于同一位置,并且具有通用類型CustomerSpecific : public final class CustomerPredicate implements Predicate<CustomerSpecific>, CustomerSpecific {private final Customer customer;// valued constructor omitted for claritypublic Customer getCustomer() {return customer;}public boolean apply(CustomerSpecific specific) {return specific.getCustomer().equals(customer);}
} 請注意,該謂詞本身可以實現CustomerSpecific接口,因此甚至可以對其進行評估! 在使用類似特征的接口時,您必須注意泛型并稍微更改類PurchaseOrders中需要Predicate <PurchaseOrder>的方法,以便它也可以接受PurchaseOrder超類型的任何謂詞: public Iterable<PurchaseOrder> selectOrders(Predicate<? super PurchaseOrder> condition) {return Iterables.filter(orders, condition);
} 域驅動設計規范 埃里克·埃文斯(Eric Evans)和馬丁·福勒(Martin Fowler)一起編寫了規范Specification ,這顯然是一個謂詞。 實際上,“謂詞”一詞是邏輯編程中使用的詞,并且編寫了規范說明用于說明我們如何將邏輯編程的某些功能借用到面向對象的語言中。 在《域驅動設計》一書中,埃里克·埃文斯(Eric Evans)詳細介紹了這種模式,并給出了幾個規范的示例,這些示例均表示域的各個部分。 就像本書所描述的策略模式一樣,當將策略模式應用于領域時,從某種意義上說,規范模式也可以被認為是領域領域謂詞的一種版本,其另外的目的是清楚地標記和標識該領域。商業規則。 作為說明,Specification模式中建議的方法名稱為: isSatisfiedBy(T):boolean ,它重點關注域約束。 正如我們之前用謂詞所見,封裝在Specification對象中的業務邏輯原子可以使用布爾邏輯(或(而不是全部)邏輯)重新組合,就像在Interpreter模式中一樣 。 該書還介紹了一些更高級的技術,例如查詢數據庫或存儲庫時的優化以及使用。 ? 查詢時的優化 以下是優化技巧,但我不確定您是否會需要它們。 但這確實是事實,謂詞在過濾數據集時非常笨拙:必須僅對集合中的每個元素進行評估,這可能會導致大型集合的性能問題。 如果將元素存儲在數據庫中并提供了謂詞,那么通過謂詞檢索每個元素以逐個過濾它們對于大型集合來說似乎并不是一個正確的主意…… 遇到性能問題時,可以啟動分析器并找到瓶頸。 現在,如果經常調用謂詞從數據結構中過濾出元素是一個瓶頸,那么如何解決呢? 一種方法是擺脫完整的謂詞,然后回到硬編碼,更易于出錯,重復且測試較少的代碼。 只要我能找到更好的替代方案來優化謂詞,我就會一直反對這種方法,并且有很多選擇。 首先,深入了解代碼的使用方式。 本著領域驅動設計的精神,每當出現問題時,就應該系統地尋找領域的見解。 通常,系統中有明確的使用模式。 盡管統計,它們為優化提供了巨大的機會。 例如,在我們的PurchaseOrders類中,檢索每個PENDING訂單的頻率可能比其他所有案例都要高得多,因為在我們的虛構示例中,從業務角度來看,這樣做才有意義。 朋友共謀 根據使用模式,您可以編寫專門針對其優化的替代實現。 在我們經常查詢待處理訂單的示例中,我們將編寫一個替代實現FastPurchaseOrder ,該實現使用一些預先計算的數據結構來使待處理訂單準備就緒以便快速訪問。 現在,為了從此替代實現中受益,您可能很想更改其接口以添加專用方法,例如selectPendingOrders() 。 請記住,在您只有通用的selectOrders(Predicate)方法之前。 添加額外的方法在某些情況下可能還不錯,但可能會引起一些問題:您還必須在其他所有實現中都實現此額外的方法,并且額外的方法可能對特定的用例而言過于具體,因此可能不適用于接口。 通過僅期望謂詞的完全相同的方法使用內部優化的技巧只是使實現認識到它所關聯的謂詞。 我稱其為“ Friend Complicity ”,是指C ++中的friend關鍵字。 /** Optimization method: pre-computed list of pending orders */
private Iterable<PurchaseOrder> selectPendingOrders() {// ... optimized stuff...
}public Iterable<PurchaseOrder> selectOrders(Predicate<? super PurchaseOrder> condition) {// internal complicity here: recognize friend class to enable optimizationif (condition instanceof PendingOrderPredicate) {return selectPendingOrders();// faster way}// otherwise, back to the usual casereturn Iterables.filter(orders, condition);
} 顯然,它增加了兩個實現類之間的耦合,否則它們應該彼此忽略。 而且,僅當直接給?friend?謂詞而沒有修飾符或復合詞時,它才有助于提高性能。 Friend Complicity真正重要的是確保方法的行為永不妥協,無論是否進行優化,都必須始終滿足接口的約定,只是性能改進可能會或不會發生。 另外請記住,您可能有一天可能要切換回未經優化的實施。 ? SQL受損 如果訂單實際上存儲在數據庫中,則可以使用SQL快速查詢它們。 順便說一句,您可能已經注意到謂詞的概念恰好是您在SQL查詢中的WHERE子句之后放置的。 仍然使用謂詞并提高性能的第一種簡單方法是,使某些謂詞使用方法asSQL()實現額外的接口SqlAware :String ,該字符串返回與謂詞本身的評估相對應的確切SQL查詢 。 當對數據庫支持的存儲庫使用謂詞時,存儲庫將調用此方法,而不是通常的validate(Predicate)或apply(Predicate)方法,然后使用返回的查詢來查詢數據庫。 我稱這種方法是SQL受損的,因為謂詞現在已被特定于數據庫的詳細信息所污染,因此應該更經常地忽略它。 直接使用SQL的替代方法包括使用存儲過程或命名查詢 :謂詞必須提供查詢的名稱及其所有參數。 在存儲庫和傳遞給它的謂詞之間進行雙調度也是一種選擇:存儲庫在其附加方法selectElements(this)上調用謂詞,該附加方法本身又調用正確的預選擇方法findByState(state):存儲庫中的Collection ; 謂詞然后對返回的集合應用其自己的過濾,并返回最終的過濾集合。 包容性 包含是一個邏輯概念,用于表達一個概念包含另一個概念的關系,例如“紅色,綠色和黃色包含在術語顏色下”( Merriam-Webster )。 謂詞之間的包含可能是在代碼中實現的非常強大的概念。 讓我們以廣播股票報價的應用程序為例。 注冊時,我們必須聲明我們有興趣觀察哪些報價。 我們可以通過簡單地傳遞一個只對我們感興趣的股票評估為真的股票謂詞來做到這一點: public final class StockPredicate implements Predicate<String> {private final Set<String> tickers;// Constructors omitted for claritypublic boolean apply(String ticker) {return tickers.contains(ticker);}} 現在,我們假設該應用程序已經廣播了有關消息傳遞主題的標準的流行行情設置,并且每個主題都有自己的謂詞。 如果它可以檢測到我們要使用的謂詞是“包括”,或包含在標準謂詞之一中,我們可以訂閱它并保存計算。 在我們的情況下,通過簡單地在謂詞上添加其他方法,這種包含就相當容易了: public boolean encompasses(StockPredicate predicate) {return tickers.containsAll(predicate.tickers);}
參考: 帶有謂詞的純Java語言中的函數式風格– Cyrille Martraire博客博客中來自JCG合作伙伴 Cyrille Martraire的第二部分 。
包含是關于評估“包含”的另一個謂詞 。 當謂詞基于集合(如示例中)或基于數字或日期的間隔時,這很容易。 否則,您可能不得不訴諸類似于Friend Complicity的技巧,即以個案的方式識別另一個謂詞,以決定是否包含該謂詞。
總體而言,請記住,一般情況下很難實現包容,但是即使部分包容也可能非常有價值,因此它是工具箱中的重要工具。 結論 謂詞很有趣,可以增強您的代碼和您的思考方式! 干杯, 該部分的單個源文件可下載cyriux_predicates_part2.zip(固定的損壞鏈接)參考: 帶有謂詞的純Java語言中的函數式風格– Cyrille Martraire博客博客中來自JCG合作伙伴 Cyrille Martraire的第二部分 。
翻譯自: https://www.javacodegeeks.com/2012/05/functional-style-in-java-with_23.html
總結
以上是生活随笔為你收集整理的带有谓词的Java中的函数样式-第2部分的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: p图模拟器手机版下载(安卓p模拟器)
- 下一篇: Java EE 6测试第I部分– EJB