函数式编程让你忘记设计模式
2019-08-16 09:55:49来源:博客园 阅读 ()
函数式编程让你忘记设计模式
本文是一篇《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("aaaddd");
System.out.println(res2);
}
}
函数式编程思路
如果使用Lambda表达式考虑,你会发现ValidationStrategy就是一个函数接口(还与Predicate
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编程实战》
- 《设计模式之禅》
本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。
原文链接:https://www.cnblogs.com/javaadu/p/11148006.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:与Swing的初见
- 老板让你抗住千万级流量,如何做架构设计? 2020-06-11
- 因为命名被diss无数次。简单聊聊编程最头疼的事情之一:命名 2020-06-10
- 今天来介绍java 各版本的新特性,一篇文章让你了解 2020-06-10
- Java3个编程题整理 2020-06-09
- (易忘篇)java基础编程难点4 2020-06-08
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash