簡介(Introduction)
之前學(xué)習(xí)Java8實(shí)戰(zhàn)時(shí)个唧,遇到一個(gè)很好的策略模式示例钥顽。便想著借著這個(gè)示例結(jié)合反饋式的方法來坟冲,學(xué)習(xí)策略設(shè)計(jì)模式睛挚,也以便后面反復(fù)琢磨學(xué)習(xí)邪蛔。
首先我們通過練習(xí),逐步寫出符合相應(yīng)需求的代碼扎狱,再根據(jù)需求進(jìn)行改進(jìn)侧到、比較、重寫淤击,最終得出一種更靈活的最佳實(shí)現(xiàn)床牧。
練習(xí)
/** 該類為蘋果 */
class Apple {
private Float weight;
private String color;
}
/** 該類為蘋果過濾器 */
public class AppleFilter {
private Set<Apple> apples;
}
- 需求一,添加方法使得可以篩選綠蘋果
- 需求二遭贸,能夠選取各種顏色的蘋果
- 需求三,能夠篩選各種顏色, 各種重量的蘋果
- 需求四心软,將篩選條件進(jìn)行抽象壕吹,能篩選各種屬性
- 需求五,使用匿名類進(jìn)行改進(jìn)
策略模式(Strategy Pattern)
對(duì)于策略模式删铃,我的理解是行為參數(shù)化耳贬。行為是指處理頻繁變化需求的那段代碼。每當(dāng)需求變化時(shí)猎唁,就傳遞不同的行為作為參數(shù)進(jìn)行處理咒劲。如此,便是將代碼塊進(jìn)行封裝诫隅,得到可進(jìn)行應(yīng)對(duì)變化的策略一般腐魂。
策略模式,它定義了算法家族逐纬。分別封裝起來蛔屹,讓它們之間可以相互替換,此模式讓算法的變換豁生,不會(huì)影響到使用算法的客戶端兔毒。——《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》
- 解決什么矛盾:不同時(shí)間應(yīng)用不同的業(yè)務(wù)規(guī)則甸箱;多重條件判斷育叁、硬編碼所帶來的復(fù)雜及難以維護(hù)
- 如何用代碼實(shí)現(xiàn):每個(gè)策略,實(shí)現(xiàn)約定的接口及方法芍殖。
- 優(yōu)點(diǎn):耦合性低(降低各種策略類與調(diào)用者的耦合)豪嗽、擴(kuò)展性強(qiáng)、代碼簡潔(策略封裝了變化的條件、避免了多重判斷)
- 缺點(diǎn):策略類膨脹昵骤、代碼繁瑣
UML
代碼實(shí)現(xiàn)
-
代碼目錄結(jié)構(gòu)
目錄結(jié)構(gòu).png 核心代碼树碱,具體詳見github
package Demo.filter;
/**
* 該類用于篩選蘋果
* 代碼質(zhì)量要求:更加抽象通用, 更加簡潔
* 以下七次的代碼修改也相應(yīng)反映代碼的質(zhì)量及水平
*
* Created by auhnayuil on 17-9-24.
*/
public class FilterApple implements Filter<Apple> {
/**
* 第一次需求:選取綠色蘋果
* 該方法純粹為篩選出綠色蘋果
* 篩選蘋果的條件為常量, 很難適應(yīng)客戶或者調(diào)用者的需求變化
*/
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;
}
/**
* 第二次需求變化:能夠選取各種顏色的蘋果
* 將顏色提取為方法的參數(shù), 更靈活地適應(yīng)篩選各種顏色的蘋果
*
* 一個(gè)良好的原則是在編寫某個(gè)需求多變的代碼時(shí), 嘗試將其抽象化
*/
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;
}
/**
* 第三次需求變化:能夠篩選各種顏色, 各種重量的蘋果
* 需求變化的因素除了單一元素上變化, 還表現(xiàn)為多元素上變化
*
* 一旦多屬性被要求組合查詢, 進(jìn)行更復(fù)雜的查詢時(shí)
* 篩選條件及使用上將會(huì)變得非常笨拙及丑陋
*/
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;
}
/**
* 第四次嘗試:將篩選條件進(jìn)行抽象, 將行為參數(shù)化
* 更高層次的抽象為將選擇條件進(jìn)行建模, 即形成一種可進(jìn)行選擇的通用的策略
*
* 模型描述:根據(jù)對(duì)象的某些屬性來返回一個(gè)布爾值
* 類似于"謂詞"這樣的語義
*
* 至于為何要在方法參數(shù)中抽象篩選條件為一個(gè)接口?
*
* 到這里, filterApples的行為僅取決于 Predicate對(duì)象所傳遞的代碼, 也就是
* 所謂的 向一個(gè)參數(shù)傳遞了代碼, 或者行為參數(shù)化了
*
* 值需要?jiǎng)?chuàng)建包裹著不同篩選條件的代碼塊 的Predicate對(duì)象就可以實(shí)現(xiàn)不同的行為了
*/
public List<Apple> filterApples(List<Apple> apples, Predicate<Apple> predicate){
return (List<Apple>) collect(apples, predicate);
}
/**
* 第五嘗試:匿名類
* 沒有變量名, 允許你同時(shí)聲明并實(shí)例化一個(gè)類
*
*/
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表達(dá)式 以及 抽象結(jié)果集
* 可以改寫為以下形式:
* filterApplesByLambda(apples, (Apple apple) -> "red".equals(apple.getColor()));
*
* 那么如何用Lambda改寫一個(gè)內(nèi)部類变秦?
*/
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;
/**
* 該方法為最基本的過濾器
* 用于抽象各個(gè)過濾器中的循環(huán), 遍歷, 收集等重復(fù)行為
* 采用接口的默認(rèn)方法實(shí)現(xiàn)
*
* 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 {
//該部分代碼塊, 通過反射生成集合的實(shí)例對(duì)象. 得到一個(gè)空的結(jié)果集對(duì)象
result = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
for(T target : targets){
//循環(huán)遍歷目標(biāo)集合, 并且通過接口形成策略判斷是否符合過濾器條件
//收集符合條件的結(jié)果
if(predicate.test(target))
result.add(target);
}
return result;
}
}
package Demo.predicate;
/**
* 策略設(shè)計(jì)模式(Staregy)
* 定義了一系列的算法族, 并將其封裝, 可以相互替換且在運(yùn)行時(shí)選擇所需要的合適的"策略"
* 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實(shí)戰(zhàn)] 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