资讯专栏INFORMATION COLUMN

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

or0fun / 838人阅读

摘要:面向对象常见的设计模式有策略模式模板方法观察者模式责任链模式以及工厂模式,使用表达式函数式编程思维有助于避免面向对象开发中的那些固定代码。

本文是一篇《Java 8实战》的阅读笔记,阅读大约需要5分钟。

有点标题党,但是这确实是我最近使用Lambda表达式的感受。设计模式是过去的一些好的经验和套路的总结,但是好的语言特性可以让开发者不去考虑这些设计模式。面向对象常见的设计模式有策略模式、模板方法、观察者模式、责任链模式以及工厂模式,使用Lambda表达式(函数式编程思维)有助于避免面向对象开发中的那些固定代码。下面我们挑选了策略模式和职责链模式两个案例进行分析。

案例1:策略模式

当我们解决一个问题有不同的解法的时候,又不希望客户感知到这些解法的细节,这种情况下适合使用策略模式。策略模式包括三个部分:

解决问题的算法(上图中的Strategy);

一个或多个该类算法的具体实现(上图中的ConcreteStrategyA、ConcreteStrategyB和ConcreteStrategyC)

一个或多个客户使用场景(上图中的ClientContext)

面向对象思路

首先定义策略接口,表示排序策略:

public interface ValidationStrategy {
    boolean execute(String s);
}

然后定义具体的实现类(即不同的排序算法):

public class IsAllLowerCase implements ValidationStrategy {
    @Override
    public boolean execute(String s) {
        return s.matches("[a-z]+");
    }
}

public class IsNumberic implements ValidationStrategy {
    @Override
    public 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("aaaffffd");
        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("aaaffffd");
        System.out.println(res2);
    }
}
案例2:责任链模式

在某些场景下,需要对一个对象做一系列的工作,这些工作分别是由不同的类完成的,这时候就比较适合使用责任链模式。责任链模式的主要组成部分包括三个:

管理操作序列的抽象类,在该抽象类里有会有一个对象记录当前对象的后继操作对象;

一些具体的操作对象,这些操作对象会以一个链表的形式组织起来

一个使用该模式的客户端组件,该组件只需要跟一个组件打交道就好,不需要跟很多个操作对象耦合在一起。

面向对象思路

首先看下我们这里定义了一个抽象类ProcessingObject,其中successor字段用于管理该对象的后继操作对象;handle接口作为对外提供服务的接口;handleWork作为实际处理对象的操作方法。

public abstract class ProcessingObject {

    protected ProcessingObject successor;
    
    public void setSuccessor(ProcessingObject 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 {
    @Override
    protected String handleWork(String input) {
        return "From Raoul, Mario and Alan: " + input;
    }
}

public class SpellCheckerProcessing extends ProcessingObject {
    @Override
    protected String handleWork(String input) {
        return input.replace("labda", "lambda");
    }
}

最后,你就可以在Client中将这上面两个具体的操作类对象构成一个操作序列,参见下面的代码:

public class Client {
    public static void main(String[] args) {
        ProcessingObject p1 = new HeaderTextProcessing();
        ProcessingObject 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 headerProcessing = (String text) -> "From Raoul, Mario and Alan: " + text;

        UnaryOperator spellCheckProcessing = (String text) -> text.replace("labda", "lambda");

        Function function = headerProcessing.andThen(spellCheckProcessing);

        String result = function.apply("Aren"t labdas really sexy?!!");
        System.out.println(result);

        UnaryOperator hhhhhProcessing = (String text) -> text.concat("hhhh");
        Function 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 headerProcessing = (String text) -> "From Raoul, Mario and Alan: " + text;
        Function1 specllCheckProcessing = (String text) -> text.replace("labda", "lambda");

        Function1 function = headerProcessing.compose(specllCheckProcessing);
        String result = function.apply("Aren"t labdas really sexy?!!");
        System.out.println(result);
    }
}
总结

可以看出,函数式编程思维跟面向对象编程思维的思考方式是不同的,表达力更强,因此,作为开发者是时候认真学习下函数式编程思维了,作为Java开发者,我准备先从Lambda表达式开始学起,然后尝试学习下Scala或Kotlin两门语言中的函数式变成特性。

参考资料

《Java编程实战》

《设计模式之禅》

本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75187.html

相关文章

  • gitbook: 前端好书推荐

    摘要:它大致概述并讨论了前端工程的实践如何学习它,以及在年实践时使用什么工具。目的是每年发布一次内容更新。前端实践第一部分广泛描述了前端工程的实践。对大多数人来说,函数式编程看起来更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 这是任何人都可以用来了解前端开发实践的指南。它大致概述并...

    Ali_ 评论0 收藏0
  • gitbook: 前端好书推荐

    摘要:它大致概述并讨论了前端工程的实践如何学习它,以及在年实践时使用什么工具。目的是每年发布一次内容更新。前端实践第一部分广泛描述了前端工程的实践。对大多数人来说,函数式编程看起来更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 这是任何人都可以用来了解前端开发实践的指南。它大致概述并...

    CocoaChina 评论0 收藏0
  • gitbook: 前端好书推荐

    摘要:它大致概述并讨论了前端工程的实践如何学习它,以及在年实践时使用什么工具。目的是每年发布一次内容更新。前端实践第一部分广泛描述了前端工程的实践。对大多数人来说,函数式编程看起来更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 这是任何人都可以用来了解前端开发实践的指南。它大致概述并...

    Warren 评论0 收藏0
  • 个人分享--web前端学习资源分享

    摘要:前言月份开始出没社区,现在差不多月了,按照工作的说法,就是差不多过了三个月的试用期,准备转正了一般来说,差不多到了转正的时候,会进行总结或者分享会议那么今天我就把看过的一些学习资源主要是博客,博文推荐分享给大家。 1.前言 6月份开始出没社区,现在差不多9月了,按照工作的说法,就是差不多过了三个月的试用期,准备转正了!一般来说,差不多到了转正的时候,会进行总结或者分享会议!那么今天我就...

    sherlock221 评论0 收藏0

发表评论

0条评论

or0fun

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<