函数式编程让你忘记设计模式
本文是一篇《Java 8實戰》的閱讀筆記,閱讀大約需要5分鐘。
有點標題黨,但是這確實是我最近使用Lambda表達式的感受。設計模式是過去的一些好的經驗和套路的總結,但是好的語言特性可以讓開發者不去考慮這些設計模式。面向對象常見的設計模式有策略模式、模板方法、觀察者模式、責任鏈模式以及工廠模式,使用Lambda表達式(函數式編程思維)有助于避免面向對象開發中的那些固定代碼。下面我們挑選了策略模式和職責鏈模式兩個案例進行分析。
案例1:策略模式
當我們解決一個問題有不同的解法的時候,又不希望客戶感知到這些解法的細節,這種情況下適合使用策略模式。策略模式包括三個部分:
- 解決問題的算法(上圖中的Strategy);
- 一個或多個該類算法的具體實現(上圖中的ConcreteStrategyA、ConcreteStrategyB和ConcreteStrategyC)
- 一個或多個客戶使用場景(上圖中的ClientContext)
面向對象思路
首先定義策略接口,表示排序策略:
public interface ValidationStrategy {boolean execute(String s); }然后定義具體的實現類(即不同的排序算法):
public class IsAllLowerCase implements ValidationStrategy {@Overridepublic boolean execute(String s) {return s.matches("[a-z]+");} }public class IsNumberic implements ValidationStrategy {@Overridepublic boolean execute(String s) {return s.matches("\\d+");} }最后定義客戶使用場景,代碼如下圖所示。Validator是為客戶提供服務時使用的上下文環境,每個Valiator對象中都封裝了具體的Strategy對象,在實際工作中,我們可以通過更換具體的Strategy對象來進行客戶服務的升級,而且不需要讓客戶進行升級。
public class Validator {private final ValidationStrategy strategy;public Validator(ValidationStrategy strategy) {this.strategy = strategy;}/*** 給客戶的接口*/public boolean validate(String s) {return strategy.execute(s);} }public class ClientTestDrive {public static void main(String[] args) {Validator numbericValidator = new Validator(new IsNumberic());boolean res1 = numbericValidator.validate("7780");System.out.println(res1);Validator lowerCaseValidator = new Validator(new IsAllLowerCase());boolean res2 = lowerCaseValidator.validate("aaaddd");System.out.println(res2);} }函數式編程思路
如果使用Lambda表達式考慮,你會發現ValidationStrategy就是一個函數接口(還與Predicate具有同樣的函數描述),那么就不需要定義上面那些實現類了,可以直接用下面的代碼替換,原因是Lambda表達式內部已經對這些類進行了一定的封裝。
public class ClientTestDrive {public static void main(String[] args) {Validator numbericValidator = new Validator((String s) -> s.matches("\\d+"));boolean res1 = numbericValidator.validate("7789");System.out.println(res1);Validator lowerCaseValidator = new Validator((String s) -> s.matches("[a-z]+"));boolean res2 = lowerCaseValidator.validate("aaaddd");System.out.println(res2);} }案例2:責任鏈模式
在某些場景下,需要對一個對象做一系列的工作,這些工作分別是由不同的類完成的,這時候就比較適合使用責任鏈模式。責任鏈模式的主要組成部分包括三個:
- 管理操作序列的抽象類,在該抽象類里有會有一個對象記錄當前對象的后繼操作對象;
- 一些具體的操作對象,這些操作對象會以一個鏈表的形式組織起來
- 一個使用該模式的客戶端組件,該組件只需要跟一個組件打交道就好,不需要跟很多個操作對象耦合在一起。
面向對象思路
首先看下我們這里定義了一個抽象類ProcessingObject,其中successor字段用于管理該對象的后繼操作對象;handle接口作為對外提供服務的接口;handleWork作為實際處理對象的操作方法。
public abstract class ProcessingObject<T> {protected ProcessingObject<T> successor;public void setSuccessor(ProcessingObject<T> successor) {this.successor = successor;}public T handler(T input) {T r = handleWork(input);if (successor != null) {return successor.handler(r);}return r;}abstract protected T handleWork(T input); }接下來可以定義兩個具體的操作對象,如下面代碼所示。PS:這里《Java 8實戰》書中用的是replaceAll方法是不太合適的,這個點可以參考我們之前的文章——020:舉幾個String的API以及案例。
public class HeaderTextProcessing extends ProcessingObject<String> {@Overrideprotected String handleWork(String input) {return "From Raoul, Mario and Alan: " + input;} }public class SpellCheckerProcessing extends ProcessingObject<String> {@Overrideprotected String handleWork(String input) {return input.replace("labda", "lambda");} }最后,你就可以在Client中將這上面兩個具體的操作類對象構成一個操作序列,參見下面的代碼:
public class Client {public static void main(String[] args) {ProcessingObject<String> p1 = new HeaderTextProcessing();ProcessingObject<String> p2 = new SpellCheckerProcessing();p1.setSuccessor(p2);String result = p1.handler("Aren't labdas really sexy?!!");System.out.println(result);} }函數式編程思路
如果使用函數式編程思維,那么職責鏈模式就直接了——y=f(x)和z=g(x)這兩個方法都是要對x做處理,那么如果將這兩個函數組合在一起,就會形成r=f(g(x))的情況,也就是可以使用Lambda表達式中的addThen來串聯起多個處理過程。
public class ClientWithLambda {public static void main(String[] args) {UnaryOperator<String> headerProcessing = (String text) -> "From Raoul, Mario and Alan: " + text;UnaryOperator<String> spellCheckProcessing = (String text) -> text.replace("labda", "lambda");Function<String, String> function = headerProcessing.andThen(spellCheckProcessing);String result = function.apply("Aren't labdas really sexy?!!");System.out.println(result);UnaryOperator<String> hhhhhProcessing = (String text) -> text.concat("hhhh");Function<String, String> function1 = function.andThen(hhhhhProcessing);String result1 = function1.apply("Aren't labdas really sexy?!!");System.out.println(result1);} }上面是利用Java原生的Lambda表達式實現的職責鏈模式,我們也可以使用前面一篇文章——vavr:讓你像寫Scala一樣寫Java中介紹過的vavr庫來實現,代碼如下所示:
public class ClientWithVavr {public static void main(String[] args) {Function1<String, String> headerProcessing = (String text) -> "From Raoul, Mario and Alan: " + text;Function1<String, String> specllCheckProcessing = (String text) -> text.replace("labda", "lambda");Function1<String, String> function = headerProcessing.compose(specllCheckProcessing);String result = function.apply("Aren't labdas really sexy?!!");System.out.println(result);} }總結
可以看出,函數式編程思維跟面向對象編程思維的思考方式是不同的,表達力更強,因此,作為開發者是時候認真學習下函數式編程思維了,作為Java開發者,我準備先從Lambda表達式開始學起,然后嘗試學習下Scala或Kotlin兩門語言中的函數式編程特性。
參考資料
本號專注于后端技術、JVM問題排查和優化、Java面試題、個人成長和自我管理等主題,為讀者提供一線開發者的工作和成長經驗,期待你能在這里有所收獲。
轉載于:https://www.cnblogs.com/javaadu/p/11148006.html
總結
以上是生活随笔為你收集整理的函数式编程让你忘记设计模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Qt Tips】QLineEdit内容
- 下一篇: 《程序员修炼之道:从小工到专家》阅读笔记