函数式编程让你忘记设计模式

  有点标题党,可是这确实是我最近使用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编程实战》
  
  《设计模式之禅》算法

相关文章
相关标签/搜索