國內(nèi)攝影作品網(wǎng)站免費(fèi)建網(wǎng)站的步驟
Java8實(shí)戰(zhàn)-總結(jié)33
- 重構(gòu)、測試和調(diào)試
- 使用 Lambda 重構(gòu)面向?qū)ο蟮脑O(shè)計(jì)模式
- 策略模式
- 模板方法
重構(gòu)、測試和調(diào)試
使用 Lambda 重構(gòu)面向?qū)ο蟮脑O(shè)計(jì)模式
新的語言特性常常讓現(xiàn)存的編程模式或設(shè)計(jì)黯然失色。比如, Java 5
中引入了for-each
循環(huán),由于它的穩(wěn)健性和簡潔性,已經(jīng)替代了很多顯式使用迭代器的情形。Java 7
中推出的菱形操作符(<>
)讓大家在創(chuàng)建實(shí)例時無需顯式使用泛型,一定程度上推動了Java
程序員們采用類型接口(type interface)進(jìn)行程序設(shè)計(jì)。
對設(shè)計(jì)經(jīng)驗(yàn)的歸納總結(jié)被稱為設(shè)計(jì)模式。設(shè)計(jì)軟件時,可以復(fù)用這些方式方法來解決一些常見問題。這看起來像傳統(tǒng)建筑工程師的工作方式,對典型的場景(比如懸掛橋、拱橋等)都定義有可重用的解決方案。例如,訪問者模式常用于分離程序的算法和它的操作對象。單例模式一般用于限制類的實(shí)例化,僅生成一份對象。
Lambda
表達(dá)式為程序員的工具箱又新添了一件利器。它們?yōu)榻鉀Q傳統(tǒng)設(shè)計(jì)模式所面對的問題提供了新的解決方案,不但如此,采用這些方案往往更高效、更簡單。使用Lambda
表達(dá)式后,很多現(xiàn)存的略顯臃腫的面向?qū)ο笤O(shè)計(jì)模式能夠用更精簡的方式實(shí)現(xiàn)了。下面會針對五個設(shè)計(jì)模式展開討論,分別是:
- 策略模式
- 模板方法
- 觀察者模式
- 責(zé)任鏈模式
- 工廠模式
策略模式
策略模式代表了解決一類算法的通用解決方案,可以在運(yùn)行時選擇使用哪種方案。例如使用不同的條件(比如蘋果的重量,或者顏色)來篩選庫存中的蘋果??梢詫⑦@一模式應(yīng)用到更廣泛的領(lǐng)域,比如使用不同的標(biāo)準(zhǔn)來驗(yàn)證輸入的有效性,使用不同的方式來分析或者格式化輸入。
策略模式包含三部分內(nèi)容:
- 一個代表某個算法的接口(它是策略模式的接口)。
- 一個或多個該接口的具體實(shí)現(xiàn),它們代表了算法的多種實(shí)現(xiàn)(比如,實(shí)體類
ConcreteStrategyA
或者ConcreteStrategyB
)。 - 一個或多個使用策略對象的客戶。
假設(shè)希望驗(yàn)證輸入的內(nèi)容是否根據(jù)標(biāo)準(zhǔn)進(jìn)行了恰當(dāng)?shù)母袷交?#xff08;比如只包含小寫字母或數(shù)字)。可以從定義一個驗(yàn)證文本(以String
的形式表示)的接口入手:
public interface ValidationStrategy { boolean execute(String s);
}
其次,定義了該接口的一個或多個具體實(shí)現(xiàn):
public class IsAllLowerCase implements ValidationStrategy { public boolean execute(String s) { return s.matches("[a-z]+"); }
}
public class IsNumeric implements ValidationStrategy { public boolean execute(String s) { return s.matches("\\d+"); }
}
之后,就可以在程序中使用這些略有差異的驗(yàn)證策略了:
public class Validator { private final ValidationStrategy strategy; public Validator(ValidationStrategy v) { this.strategy = v;} public boolean validate(String s) { return strategy.execute(s); }
} Validator numericValidator = new Validator(new IsNumeric());
boolean b1 = numericValidator.validate("aaaa"); //返回false
Validator lowerCaseValidator = new Validator(new IsAllLowerCase());
boolean b2 = lowerCaseValidator.validate("bbbb"); //返回true
使用Lambda表達(dá)式
ValidationStrategy
是一個函數(shù)接口(除此之外,它還與Predicate<String>
具有同樣的函數(shù)描述)。這意味著不需要聲明新的類來實(shí)現(xiàn)不同的策略,通過直接傳遞Lambda
表達(dá)式就能達(dá)到同樣的目的,并且還更簡潔:
Validator numericValidator = new Validator((String s) -> s.matches("[a-z]+"));
boolean b1 = numericValidator.validate("aaaa"); //直接傳遞Lambda表達(dá)式
Validator lowerCaseValidator = new Validator((String s) -> s.matches("\\d+")); //直接傳遞Lambda表達(dá)式
boolean b2 = lowerCaseValidator.validate("bbbb");
Lambda
表達(dá)式避免了采用策略設(shè)計(jì)模式時僵化的模板代碼。如果仔細(xì)分析一下個中緣由,會發(fā)現(xiàn),Lambda
表達(dá)式實(shí)際已經(jīng)對部分代碼(或策略)進(jìn)行了封裝,而這就是創(chuàng)建策略設(shè)計(jì)模式的初衷。因此,建議對類似的問題,應(yīng)該盡量使用Lambda
表達(dá)式來解決。
模板方法
如果需要采用某個算法的框架,同時又希望有一定的靈活度,能對它的某些部分進(jìn)行改進(jìn),那么采用模板方法設(shè)計(jì)模式是比較通用的方案。換句話說,模板方法模式在你“希望使用這個算法,但是需要對其中的某些行進(jìn)行改進(jìn),才能達(dá)到希望的效果”時是非常有用的。
從一個例子著手,看看這個模式是如何工作的。假設(shè)需要編寫一個簡單的在線銀行應(yīng)用。通常,用戶需要輸入一個用戶賬戶,之后應(yīng)用才能從銀行的數(shù)據(jù)庫中得到用戶的詳細(xì)信息,最終完成一些讓用戶滿意的操作。不同分行的在線銀行應(yīng)用讓客戶滿意的方式可能還略有不同,比如給客戶的賬戶發(fā)放紅利,或者僅僅是少發(fā)送一些推廣文件。可能通過下面的抽象類方式來實(shí)現(xiàn)在線銀行應(yīng)用:
abstract class OnlineBanking { public void processCustomer(int id){ Customer c = Database.getCustomerWithId(id); makeCustomerHappy(c); }abstract void makeCustomerHappy(Customer c);
}
processCustomer
方法搭建了在線銀行算法的框架:獲取客戶提供的ID
,然后提供服務(wù)讓用戶滿意。不同的支行可以通過繼承OnlineBanking
類,對該方法提供差異化的實(shí)現(xiàn)。
使用Lambda表達(dá)式
使用Lambda
表達(dá)式同樣也可以解決這些問題(創(chuàng)建算法框架,讓具體的實(shí)現(xiàn)插入某些部分)。你想要插入的不同算法組件可以通過Lambda
表達(dá)式或者方法引用的方式實(shí)現(xiàn)。
這里向processCustomer
方法引入了第二個參數(shù),它是一個Consumer<Customer>
類型的參數(shù),與前文定義的makeCustomerHappy
的特征保持一致:
public void processCustomer(int id, Consumer<Customer> makeCustomerHappy) { Customer c = Database.getCustomerWithId(id); makeCustomerHappy.accept(c);
}
現(xiàn)在,可以很方便地通過傳遞Lambda
表達(dá)式,直接插入不同的行為,不再需要繼承OnlineBanking
類了:
new OnlineBankingLambda().processCustomer(1337, (Customer c) -> System.out.println("Hello " + c.getName());