函數式編程讓你忘記設計模式

  有點標題黨,可是這確實是我最近使用Lambda表達式的感覺。設計模式是過去的一些好的經驗和套路的總結,可是好的語言特性可讓開發者不去考慮這些設計模式。面向對象常見的設計模式有策略模式、模板方法、觀察者模式、責任鏈模式以及工廠模式,使用Lambda表達式(函數式編程思惟)有助於避免面向對象開發中的那些固定代碼。下面咱們挑選了策略模式和職責鏈模式兩個案例進行分析。
  
  案例1:策略模式
  
  策略設計模式
  
  當咱們解決一個問題有不一樣的解法的時候,又不但願客戶感知到這些解法的細節,這種狀況下適合使用策略模式。策略模式包括三個部分:
  
  解決問題的算法(上圖中的Strategy);
  
  一個或多個該類算法的具體實現(上圖中的ConcreteStrategyA、ConcreteStrategyB和ConcreteStrategyC)
  
  一個或多個客戶使用場景(上圖中的ClientContext)
  
  面向對象思路
  
  首先定義策略接口,表示排序策略:
  
  public interface ValidationStrategy {
  
  boolean execute(String s);
  
  }
  
  而後定義具體的實現類(即不一樣的排序算法):
  
  public class IsAllLowerCase implements www.chaoyueylgw.com ValidationStrategy {
  
  @Override
  
  public boolean execute(String s) {
  
  return s.matches("[a-z]+");
  
  }
  
  }
  
  public class IsNumberic implements ValidationStrategy {
  
  @Override
  
  public boolean execute(String www.yifa5yl.com) {
  
  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(www.mojie1yuLe.com);
  
  }
  
  }
  
  public class ClientTestDrive {
  
  public static void main(String[www.zzhehong.com] args) {
  
  Validator numbericValidator =www.zheshengyuLe.com new Validator(new IsNumberic());
  
  boolean res1 = numbericValidator.validate("7780");
  
  System.out.www.oushengyule.com println(www.cjyl1yule.com 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> {
  
  @Override
  
  protected String handleWork(String input) {
  
  return "From Raoul, Mario and Alan: " + input;
  
  }
  
  }
  
  public class SpellCheckerProcessing extends ProcessingObject<String> {
  
  @Override
  
  protected 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兩門語言中的函數式編程特性。
  
  參考資料
  
  《Java編程實戰》
  
  《設計模式之禪》算法

相關文章
相關標籤/搜索