簡介(Introduction) <! 出這邊文章的目的,及如何更好地閱讀這篇文章 之前學習 "Java8實戰" 時,遇到一個很好的策略模式示例。便想著藉著這個示例結合反饋式的方法來,學習策略設計模式,也以便後面反覆琢磨學習。 首先我們通過練習,逐步寫出符合相應需求的代碼,再根據需求進行改進、比較、 ...
簡介(Introduction)
之前學習Java8實戰時,遇到一個很好的策略模式示例。便想著藉著這個示例結合反饋式的方法來,學習策略設計模式,也以便後面反覆琢磨學習。
首先我們通過練習,逐步寫出符合相應需求的代碼,再根據需求進行改進、比較、重寫,最終得出一種更靈活的最佳實現。
練習
/** 該類為蘋果 */
class Apple {
private Float weight;
private String color;
}
/** 該類為蘋果過濾器 */
public class AppleFilter {
private Set<Apple> apples;
}
- 需求一,添加方法使得可以篩選綠蘋果
- 需求二,能夠選取各種顏色的蘋果
- 需求三,能夠篩選各種顏色, 各種重量的蘋果
- 需求四,將篩選條件進行抽象,能篩選各種屬性
- 需求五,使用匿名類進行改進
策略模式(Strategy Pattern)
對於策略模式,我的理解是行為參數化。行為是指處理頻繁變化需求的那段代碼。每當需求變化時,就傳遞不同的行為作為參數進行處理。如此,便是將代碼塊進行封裝,得到可進行應對變化的策略一般。
策略模式,它定義了演算法家族。分別封裝起來,讓它們之間可以相互替換,此模式讓演算法的變換,不會影響到使用演算法的客戶端。——《設計模式:可復用面向對象軟體的基礎》
- 解決什麼矛盾:不同時間應用不同的業務規則;多重條件判斷、硬編碼所帶來的複雜及難以維護
- 如何用代碼實現:每個策略,實現約定的介面及方法。
- 優點:耦合性低(降低各種策略類與調用者的耦合)、擴展性強、代碼簡潔(策略封裝了變化的條件、避免了多重判斷)
- 缺點:策略類膨脹、代碼繁瑣
UML
代碼實現
代碼目錄結構
核心代碼,具體詳見github
package Demo.filter;
//
// 該類用於篩選蘋果
// 代碼質量要求:更加抽象通用, 更加簡潔
// 以下七次的代碼修改也相應反映代碼的質量及水平
//
// Created by auhnayuil on 17-9-24.
//
public class FilterApple implements Filter<Apple> {
//
// 第一次需求:選取綠色蘋果
// 該方法純粹為篩選出綠色蘋果
// 篩選蘋果的條件為常量, 很難適應客戶或者調用者的需求變化
//
public Set<Apple> filterGreenApple(Set<Apple> apples){
Set<Apple> result = new HashSet<>();
for(Apple apple : apples){
if("green".equals(apple.getColor()))
result.add(apple);
}
return result;
}
//
// 第二次需求變化:能夠選取各種顏色的蘋果
// 將顏色提取為方法的參數, 更靈活地適應篩選各種顏色的蘋果
//
// 一個良好的原則是在編寫某個需求多變的代碼時, 嘗試將其抽象化
///
public Set<Apple> filterAppleByColor(Set<Apple> apples, String color){
Set<Apple> result = new HashSet<>();
for(Apple apple : apples){
if(apple.getColor().equals(color))
result.add(apple);
}
return result;
}
//
// 第三次需求變化:能夠篩選各種顏色, 各種重量的蘋果
// 需求變化的因素除了單一元素上變化, 還表現為多元素上變化
//
// 一旦多屬性被要求組合查詢, 進行更複雜的查詢時
// 篩選條件及使用上將會變得非常笨拙及醜陋
///
public Set<Apple> filterApples(Set<Apple> apples, String color, Float weight){
Set<Apple> result = new HashSet<>();
for(Apple apple : apples){
if( apple.getColor().equals(color)
&& apple.getWeight() > weight)
result.add(apple);
}
return result;
}
//
// 第四次嘗試:將篩選條件進行抽象, 將行為參數化
// 更高層次的抽象為將選擇條件進行建模, 即形成一種可進行選擇的通用的策略
//
// 模型描述:根據對象的某些屬性來返回一個布爾值
// 類似於"謂詞"這樣的語義
//
// 至於為何要在方法參數中抽象篩選條件為一個介面?
//
// 到這裡, filterApples的行為僅取決於 Predicate對象所傳遞的代碼, 也就是
// 所謂的 向一個參數傳遞了代碼, 或者行為參數化了
//
// 值需要創建包裹著不同篩選條件的代碼塊 的Predicate對象就可以實現不同的行為了
///
public List<Apple> filterApples(List<Apple> apples, Predicate<Apple> predicate){
return (List<Apple>) collect(apples, predicate);
}
//
// 第五嘗試:匿名類
// 沒有變數名, 允許你同時聲明並實例化一個類
//
///
public Set<Apple> filterApplesByAnonymousClass(Set<Apple> apples){
return (Set<Apple>) collect(apples, new Predicate<Apple>() {
@Override
public boolean test(Apple target) {
return ("red".equals(target.getColor()) && target.getWeight() > 0.0F);
}
});
}
//
// 第六次嘗試:Lambda表達式 以及 抽象結果集
// 可以改寫為以下形式:
// filterApplesByLambda(apples, (Apple apple) -> "red".equals(apple.getColor()));
//
// 那麼如何用Lambda改寫一個內部類?
//
public Set<Apple> filterApplesByLambda(Collection<Apple> apples, boolean is){
Set<Apple> result = new HashSet<>();
for(Apple apple : apples){
if(is)
result.add(apple);
}
return result;
}
}
package Demo.filter;
//
// 該方法為最基本的過濾器
// 用於抽象各個過濾器中的迴圈, 遍歷, 收集等重覆行為
// 採用介面的預設方法實現
//
// Created by auhnayuil on 17-9-24.
//
public interface Filter<T> {
default Collection<T> collect(Collection<T> targets, Predicate<T> predicate) {
Class<? extends Collection> clazz = targets.getClass();
Collection result = null;
try {
//該部分代碼塊, 通過反射生成集合的實例對象. 得到一個空的結果集對象
result = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
for(T target : targets){
//迴圈遍歷目標集合, 並且通過介面形成策略判斷是否符合過濾器條件
//收集符合條件的結果
if(predicate.test(target))
result.add(target);
}
return result;
}
}
package Demo.predicate;
//
// 策略設計模式(Staregy)
// 定義了一系列的演算法族, 並將其封裝, 可以相互替換且在運行時選擇所需要的合適的"策略"
// Created by auhnayuil on 17-9-24.
//
public class AppleRedAndWeightPrdicate implements Predicate<Apple> {
@Override
public boolean test(Apple target) {
return ("red".equals(target.getColor())
&& target.getWeight() > 0.0F);
}
}
參考鏈接
[Java8實戰] https://book.douban.com/subject/26772632/
[Baidu] https://baike.baidu.com/item/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F/646307?fr=aladdin
[菜鳥教程] http://www.runoob.com/design-pattern/strategy-pattern.html