10个步骤的筛选器模式
過濾器是僅在某些情況下應應用的模式。 在原始帖子中 ,我提供了一個非常簡單的示例,旨在演示如何應用它。 在這篇文章中,我提供了一個更詳細的示例,該示例還旨在說明何時以及為什么應用它。
介紹
該職位包括以下10個簡短步驟 。 在每個步驟中,我介紹了以下兩種類型的要求 :
- B- * : 業務要求(由產品所有者提供→ 無可爭辯 )
- S- * : 解決方案要求(從解決方案的選擇→ 有爭議的結果中得出)
我提出了一個Java模型,該模型可以滿足到目前為止介紹的要求。 我一直這樣做,直到Filterer成為首選解決方案。
所以,讓我帶你踏上這段旅程……
步驟1:問題檢測器
要求#1
假設企業要求一種算法來檢測英文文本中的語法和拼寫問題 。
例如:
- 文字: 您很了解。 →要檢測的問題:
- migth (類型:拼寫)
- 文字: 我沒有注意放松。 →要檢測的問題:
- 注意 (類型:拼寫)
- 松散 (類型:語法)
- 文字: 我一直注意到它很松。 →要檢測的問題:?
這是第一個業務需求 ( B-1 )。
B-1會議的最簡單模型可以是:
- 輸入 :純文本
- 輸出 :問題列表,每個問題提供:
- 輸入文字內的偏移量
這是我們的第一個解決方案要求 ( S-1 )。
Java模型#1
我們可以將S-1建模為:
interface IssueDetector {// e.g. text: "You migth know it."List<Issue> detect(String text); }哪里:
interface Issue {int startOffset(); // e.g. 4 (start of "migth")int endOffset(); // e.g. 9 (end of "migth")IssueType type(); // e.g. SPELLING }enum IssueType { GRAMMAR, SPELLING }提交1 。
步驟2:機率
要求#2
但是,要實現以這種確定性方式工作的真實IssueDetector相當困難:
- 問題(概率P = 100% )
- 無問題(概率P = 0% )
相反, IssueDetector應該是概率性的 :
- 可能的問題(概率P =? )
我們可以通過引入概率閾值( PT )來保持問題/非問題的區別:
- 問題(概率P≥PT ),
- 非問題(概率P <PT )。
盡管如此,仍然值得修改模型以保留概率( P )–例如,在渲染 (更高的概率→更突出的渲染)中很有用。
綜上,我們額外的解決方案要求是:
- S-2 :支持發布概率( P );
- S-3 :支持概率閾值( PT )。
Java模型2
通過將probability()添加到Issue我們可以滿足S-2 :
interface Issue {// ...double probability(); }我們可以通過在IssueDetector添加probabilityThreshold IssueDetector probabilityThreshold來滿足S-3的IssueDetector :
interface IssueDetector {List<Issue> detect(String text, double probabilityThreshold); }提交2 。
步驟3:可能的問題
要求#3
假設業務需要 :
- B-3 :使用英語語言家校對的文本測試所有問題檢測器(=無概率)。
這樣的校對文本 (或: 測試用例 )可以定義為:
- 文字,例如您shuold知道。
- 預期的問題,例如
- shuold (類型:拼寫)
因此,我們的解決方案要求是:
- S-4 :支持預期的問題(=沒有可能性)。
Java模型3
我們可以通過提取子接口( ProbableIssue )來滿足S-4 :
interface ProbableIssue extends Issue {double probability(); }并從IssueDetector返回ProbableIssue :
interface IssueDetector {List<ProbableIssue> detect(...); }提交3 。
步驟4:明智的文字
要求#4
假使,假設:
通常,一個測試用例代表了一種我們可以稱之為按問題發布的文本 (一種文本及其問題)。
為了避免將有問題的文本建模為Map.Entry<String, List<Issue>> (含糊不清,表示抽象不足),讓我們介紹另一個解決方案要求 :
- S-5 :支持明智的文本。
Java模型4
我們可以將S-5建模為:
interface IssueWiseText {String text(); // e.g. "You migth know it."List<Issue> issues(); // e.g. ["migth"] }這使我們可以簡單地定義測試用例Stream ,如下所示:
- Stream<IssueWiseText>
代替
- Stream<Map.Entry<String, List<Issue>>> 。
提交4 。
步驟5:預期覆蓋率
要求#5
假設業務需要 :
- B-4 :報告一系列測試用例的 預期 問題范圍 ;
為了簡單起見,將問題的覆蓋范圍定義為:
總發行時長
─────────────
總文字長度
實際上, 問題的覆蓋范圍可能代表一些非常復雜的業務邏輯 。
Java模型5
我們可以使用基于Collector的方法處理B-4 :
static double issueCoverage(Stream<? extends IssueWiseText> textStream) {return textStream.collect(IssueCoverage.collector()); }Collector基于具有兩個可變字段的Accumulator :
int totalIssueLength = 0; int totalTextLength = 0;對于每個IssueWiseText ,我們增加:
totalIssueLength += issueWiseText.issues().stream().mapToInt(Issue::length).sum(); totalTextLength += issueWiseText.text().length();然后我們將問題覆蓋范圍計算為:
(double) totalIssueLength / totalTextLength提交5 。
步驟6:取得覆蓋
要求#6
假設業務需要 :
- B-5 :報告獲得了整個測試集的問題覆蓋率 。
“獲得”是指“使用檢測到的問題進行計算”。 現在事情開始變得有趣了!
首先,由于IssueCoverage表示業務邏輯 ,所以我們不應該重復它:
- S-6 :重用問題覆蓋代碼。
其次,由于該方法采用Stream<? extends IssueWiseText> Stream<? extends IssueWiseText> ,我們需要為ProbableIssue建模一個IssueWiseText :
- S-7 :支持概率問題文本。
我在這里只看到兩個選擇:
參數Java模型#6
S-7的參數模型很簡單-我們在IssueWiseText需要<I extends Issue> IssueWiseText <I extends Issue> (一個有界類型參數 ):
interface IssueWiseText<I extends Issue> {String text();List<I> issues(); }這個模型有缺點(例如類型擦除 ),但是很簡潔。
我們還可以修改IssueDetector來返回IssueWiseText<ProbableIssue> 。
此外,我們的測試用例Stream可能會變成Stream<IssueWiseText<Issue>> (盡管IssueWiseText<Issue>頗有爭議)。
提交6a 。
子類型化Java模型#6
另一個選擇是選擇子類型 (它有其自身的缺點,其中最大的缺點可能是重復 )。
S-7的子類型模型采用返回類型協方差 :
interface ProbabilisticIssueWiseText extends IssueWiseText {@OverrideList<? extends ProbableIssue> issues(); }IssueWiseText中的issues()必須成為上限 ( List<? extends Issue> )。
我們還可以修改IssueDetector來返回ProbabilisticIssueWiseText 。
提交6b 。
步驟7:按問題類型過濾
要求#7
假設業務需要 :
- B-6 :按問題類型報告問題范圍 。
我們可以通過接受Predicate <? super Issue>類型的額外參數來支持它Predicate <? super Issue> Predicate <? super Issue> ( IssueType參數通常太窄)。
但是,直接在IssueCoverage支持它會使業務邏輯復雜化( 提交7a' )。 相反,我們寧愿將已過濾的IssueWiseText實例提供給IssueCoverage 。
我們如何進行過濾? “手動”執行操作(調用new )會給實現帶來不必要的耦合(我們甚至還不了解它們)。 這就是為什么我們讓IssueWiseText進行過濾的原因(我覺得這個邏輯屬于那里):
- S-8 :支持在IssueWiseText按Issue進行IssueWiseText 。
換句話說,我們希望能夠說:
換句話說,我們希望能夠說:
嘿IssueWiseText ,按Issue篩選自己!
參數Java模型#7
在參數模型中,我們將以下filtered方法添加到IssueWiseText<I>
IssueWiseText<I> filtered(Predicate<? super I> issueFilter);這使我們滿足B-6的要求:
return textStream.map(text -> text.filtered(issue -> issue.type() == issueType)).collect(IssueCoverage.collector());提交7a 。
子類型化Java模型#7
在子類型模型中,我們還添加了filtered方法(與上面的方法非常相似):
IssueWiseText filtered(Predicate<? super Issue> issueFilter);這讓我們以與上述相同的方式遇到了B-6 。
提交7b 。
步驟8:按機率篩選
要求#8
假設業務需要 :
- B-7 :按最小概率報告問題的覆蓋范圍 。
換句話說,企業希望知道概率分布如何影響問題的覆蓋范圍。
現在,我們不希望運行IssueDetector與許多不同的概率閾值(PT),因為這將會是非常低效的。 相反,我們將只運行一次( PT = 0 ),然后以最低的概率繼續丟棄問題,以重新計算問題的覆蓋范圍。
但是,為了能夠按概率進行過濾,我們需要:
- S-9 :支持在概率問題文本中按ProbableIssue進行過濾。
參數Java模型#8
在參數模型中,我們不需要更改任何內容。 我們可以滿足B-7的要求:
return textStream.map(text -> text.filtered(issue -> issue.probability() >= minProbability)).collect(IssueCoverage.collector());提交8a 。
子類型化Java模型#8
在子類型化模型中,這比較困難,因為我們需要在ProbabilisticIssueWiseText一個額外的方法:
ProbabilisticIssueWiseText filteredProbabilistic(Predicate<? super ProbableIssue> issueFilter);讓我們滿足B-7的要求:
return textStream.map(text -> text.filteredProbabilistic(issue -> issue.probability() >= minProbability)).collect(IssueCoverage.collector());提交8b 。
對我來說, ProbabilisticIssueWiseText這種額外方法非常令人不安(請參閱此處 )。 這就是為什么我提議…
步驟9:篩選器
要求#9
由于子類型模型中的常規過濾是如此“不一致”,因此讓我們使其統一:
- S-10 :在問題型文本的子類型模型中支持統一過濾。
換句話說,我們希望能夠說:
嘿ProbabilisticIssueWiseText ,用ProbableIssue過濾自己(但是與IssueWiseText本身通過Issue過濾自己一樣)!
據我所知,這只能通過Filterer Pattern實現 。
子類型化Java模型#9
因此,我們將通用 Filterer應用于IssueWiseText :
Filterer<? extends IssueWiseText, ? extends Issue> filtered();以及ProbablisticIssueWiseText :
@Override Filterer<? extends ProbabilisticIssueWiseText, ? extends ProbableIssue> filtered();現在,我們可以通過調用以下內容進行統一過濾:
text.filtered().by(issue -> ...)提交9 。
步驟10:檢測時間
到這個時候,您必須想知道如果參數化模型這么簡單的話,為什么還要打擾子類型化模型。
因此,最后一次,我們假設業務需要 :
- B-8 :報告檢測時間 (=檢測給定文本中所有問題所花費的時間)。
參數Java模型#10
我僅看到將B-8合并到參數模型中的兩種方法:1)組成,2)子類型化。
參數Java模型#10的組成
涂抹組合物很容易。 我們介紹IssueDetectionResult :
interface IssueDetectionResult {IssueWiseText<ProbableIssue> probabilisticIssueWiseText();Duration detectionTime(); }并修改IssueDetector以將其返回。
提交10a 。
參數Java模型#10的子類型化
應用子類型需要更多的工作。 我們需要添加ProbabilisticIssueWiseText<I> *
interface ProbabilisticIssueWiseText<I extends ProbableIssue> extends IssueWiseText<I> {Duration detectionTime();// ... }并修改IssueDetector以返回ProbabilisticIssueWiseText<?> 。
提交10a' 。
*請注意,我在ProbabilisticIssueWiseText上保留了<I> ,以便不以危險的方式將參數化與子類型化相關聯 。
子類型化Java模型#10
使用純子類型模型,合并B-8非常容易。 我們只是將detectionTime()添加到ProbabilisticIssueAwareText :
interface ProbabilisticIssueWiseText extends IssueWiseText {Duration detectionTime();// ... }提交10b 。
結論
沒有時間詳細討論了(該帖子已經比我預期的要長)。
但是,與其他解決方案Filterer ,我更喜歡純子類型化(因此更喜歡Filterer ),因為:
我說“太多自由度”,我只需要:
- IssueAwareText<?>
- ProbabilisticIssueAwareText<?>
- IssueAwareText<Issue> (有爭議)
但在代碼中,我也會遇到(根據經驗說!):
- IssueAwareText<? extends Issue> IssueAwareText<? extends Issue> (冗余上限)
- IssueAwareText<ProbableIssue>
- IssueAwareText<? extends ProbableIssue> IssueAwareText<? extends ProbableIssue> (為什么不ProbabilisticIssueAwareText<?> ?)
- ProbabilisticIssueAwareText<? extends ProbableIssue> ProbabilisticIssueAwareText<? extends ProbableIssue> (冗余上限)
- ProbabilisticIssueAwareText<ProbableIssue>
所以對我來說太混亂了。 但是,如果您真的對此主題感興趣,請查看“ 復雜子類型與參數化” (不過請注意,它甚至比這篇文章還要長!)。
感謝您的閱讀!
翻譯自: https://www.javacodegeeks.com/2019/02/filterer-pattern-10-steps.html
總結
以上是生活随笔為你收集整理的10个步骤的筛选器模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Apache Camel 2.23发布
- 下一篇: ddos是病毒吗(ddos病毒的来源)