基于lambda简化设计模式

前言

虽说使用设计模式可以让复杂的业务代码变得清晰且易于维护,但是某些情况下,开发可能会遇到我为了简单的业务逻辑去适配设计模式的情况,本文笔者就以四种常见的设计模式为例,演示如何基于lambda来简化设计模式的实现。

策略模式

我们的项目中会涉及各种各样的校验,可能是校验电话号码、单双数、字符串长度等,为此我们希望通过策略模式来封装这些校验规则。

第一步自然是通过接口来定义策略,编写一个名为execute方法,让用户传入字符串,返回校验结果的布尔值:

/*** 定义策略模式的接口*/
public interface ValidationStrategy {/*** 校验该字符串是否符合要求,若符合则返回true* @param str* @return*/boolean execute(String str);
}

然后将这个策略接口聚合到我们的校验器中,后续我们就可以按照需求传入对应的校验策略即可:

/*** 校验工具,将策略接口成员成员属性,起到依赖抽象的作用*/
public class Validator {private ValidationStrategy strategy;public Validator() {}public Validator(ValidationStrategy strategy) {this.strategy = strategy;}public boolean validate(String str) {return strategy.execute(str);}
}

假如我们需要校验这个字符串是否全为字符串小写,那么我们就可以封装这样一个类:

/*** 判断是否全为小写*/
public class IsAllLowerCase implements ValidationStrategy {@Overridepublic boolean execute(String str) {return str.matches("[a-z]+");}
}

同理,如果我们需要判断是否全为数字,则可以这样写:

/*** 判断传入字符是否全为数字*/
public class IsNumeric implements ValidationStrategy {@Overridepublic boolean execute(String str) {return str.matches("\\d+");}
}

使用时,我们只需按需传入校验规则即可:

public class Main {public static void main(String[] args) {//校验是否全为数字Validator v1 = new Validator(new IsNumeric());System.out.println(v1.validate("1234"));//校验是否全是小写Validator v2 = new Validator(new IsAllLowerCase());System.out.println(v2.validate("dalhl"));}
}

输出结果如下:

true
true

不知道读者是否可以发现问题,规则的校验往往只是一两行代码,为了适配规则校验所用到的策略模式,开发者往往需要对此额外创建一个类,要知道字符校验的规则是成百上千的,并且很多校验规则很可能仅仅是某个业务才会用到的。

所以我们是否有办法做到既能适配策略模式,又避免为了一段简单的校验代码而去创建一个类呢?

查看我们校验策略接口ValidationStrategy的定义,它要求传入一个String返回一个boolean,由此我们想到了java8提供的函数时接口Function,其定义如下所示,可以根据泛型要求要指明泛型TRapply方法要求传入一个T,这里可以直接理解为我们的String,然后返回一个R,同理代入我们的boolean:

@FunctionalInterface
public interface Function<T, R> {/*** Applies this function to the given argument.** @param t the function argument* @return the function result*/R apply(T t);.......}

按照java8lambda语法糖,Function<T, R>只有一个需要实现的方法R apply(T t),我们完全可以表面类的创建,取而代之的是这样一段表达式:

t->R

查看我们ValidationStrategy的定义,它也是只有一个方法execute,我们完全可以将其视为Function<String, Boolean>,即可得表达式s->boolean

在这里插入图片描述

由此我们得出下面这段代码,可以看到根据接口的定义匹配java8对应的函数时接口,然后基于lambda表达式即可完成创建,这样做法避免了类的生命,避免了简单逻辑复杂化实现的问题:

public static void main(String[] args) {//校验是否全为数字Validator v1 = new Validator((s) -> s.matches("\\d+"));System.out.println(v1.validate("1234"));//校验是否全是小写Validator v2 = new Validator(s -> s.matches("[a-z]+"));System.out.println(v2.validate("dalhl"));}

模板方法

银行接待VIP顾客的核心流程为:

  1. 查询顾客是否是VIP
  2. 招待顾客,为顾客办理业务。

所有银行的大体流程都是这样,唯一的区别就是第2步,对此我们可以使用模板方法模式,创建一个抽象类,将第1步抽出来,而第2步按照不同银行进行不同的实现:

public abstract class Banking {public void processCustomer(int id) {//查询会员名String customer = getCustomerWithId(id);//招待会员makeCustomerHappy(customer);}private String getCustomerWithId(int id) {return RandomUtil.randomString(5);}protected abstract void makeCustomerHappy(String customer);
}

对应两个银行的实现代码,先来看看BankingA 的招待逻辑:

public class BankingA extends Banking {@Overrideprotected void makeCustomerHappy(String customer) {System.out.println("请"+customer+"吃饭,并为其办理业务");}
}

BankingB的招待逻辑:

public class BankingB extends Banking {@Overrideprotected void makeCustomerHappy(String customer) {System.out.println("请" + customer + "喝茶,并为其办理业务");}
}

测试代码如下:

public static void main(String[] args) {BankingA bankingA = new BankingA();bankingA.processCustomer(1);BankingB bankingB= new BankingB();bankingB.processCustomer(1);}

对应输出结果:

6brkb吃饭,并为其办理业务
请autjm喝茶,并为其办理业务

还是一样的问题,找到会员是一段无返回值的简单输出,为了适配模板方法,这一行代码也还是要创建一个类,所以我们还是需要用lambda对其进行简化。

查看抽象方法makeCustomerHappy的定义,它要求传入一个传入而返回一个void,查阅java8对应的函数式接口,我们找到了Consumer

@FunctionalInterface
public interface Consumer<T> {/*** Performs this operation on the given argument.** @param t the input argument*/void accept(T t);

于是我们得出公式s->Void

在这里插入图片描述

对此我们将抽象类Banking 加以改造,将抽象方法makeCustomerHappy改为Consumer接口:

public abstract class Banking {public void processCustomer(int id, Consumer<String> makeCustomerHappy) {//查询会员名String customer = getCustomerWithId(id);//招待会员makeCustomerHappy.accept(customer);}private String getCustomerWithId(int id) {return RandomUtil.randomString(5);}}

这样一来,后续的调用即可用一段lambda实现:

public class Main {public static void main(String[] args) {Banking bankingA = new Banking();bankingA.processCustomer(1,customer-> System.out.println("请"+customer+"吃饭,并为其办理业务"));Banking bankingB = new Banking();bankingB.processCustomer(1,customer-> System.out.println("请"+customer+"喝茶,并为其办理业务"));}
}

观察者模式

观察者模式算是最经典也最好理解的设计模式,观察者只需将自己注册到感兴趣的主题上,一旦有主题更新就会及时通知观察者,观察者按照自己的需要进行响应处理。

对此我们首先定义观察者的接口:

/*** 观察者*/
public interface Observer {void inform(String msg);
}

接下来就是主题:

public interface Subject {void registerObserver(Observer observer);void notifyObserver();
}

创建一个观察者1以及观察者2以及实现他们对自己感兴趣主题时会做出的反馈输出方法inform:

public class Observer1 implements Observer {@Overridepublic void inform(String msg) {System.out.println("观察者1收到通知,内容为:" + msg);}
}
public class Observer2 implements Observer {@Overridepublic void inform(String msg) {System.out.println("观察者2收到通知,内容为:" + msg);}
}

最后就是主题类的实现,我们将观察者聚合,如果观察者对SubJect1 感兴趣,则通过registerObserver完成注册,一旦主题要发布新消息就可以通过notifyObserver及时通知每一个订阅者:

public class SubJect1 implements Subject {private String msg;public SubJect1(String msg) {this.msg = msg;}private List<Observer> observerList = new ArrayList<>();@Overridepublic void registerObserver(Observer observer) {observerList.add(observer);}@Overridepublic void notifyObserver() {observerList.forEach(o -> o.inform(msg));}
}

测试代码和对应输出结果如下所示:

public static void main(String[] args) {SubJect1 subJect1 = new SubJect1("请大家学习《基于lambda简化设计模式》");//注册订阅者subJect1.registerObserver(new Observer1());subJect1.registerObserver(new Observer2());//主题发起通知subJect1.notifyObserver();}

输出结果:

观察者1收到通知,内容为:请大家学习《基于lambda简化设计模式》
观察者2收到通知,内容为:请大家学习《基于lambda简化设计模式》

很明显的Observer的inform是典型的Consumer接口,我们直接将其简化:

public static void main(String[] args) {SubJect1 subJect1 = new SubJect1("请大家学习《基于lambda简化设计模式》");//注册订阅者subJect1.registerObserver(s -> System.out.println("观察者1收到消息" + s));subJect1.registerObserver(s -> System.out.println("观察者2收到消息" + s));//主题发起通知subJect1.notifyObserver();}

责任链模式

我们希望字符串被对象1处理完成之后要转交给对象2处理,并且我们后续可能还会交给更多的对象处理,通过对需求的梳理和抽象,这个功能完全可以通过责任链模式来实现。

首先声明公共抽象类,可以看到考虑类的通用性笔者将这个类的入参设置为泛型,并且公共方法handle的步骤为:

  1. 调用自己的handWork处理输入数据,handWork交给实现类自行编写。
  2. successor不为空,则将处理结果交给下一个处理器处理,由此构成一条处理链。
public abstract class ProcessingObject<T> {/*** 下一个处理器*/private ProcessingObject<T> successor;public ProcessingObject<T> getSuccessor() {return successor;}public void setSuccessor(ProcessingObject<T> successor) {this.successor = successor;}public T handle(T input) {//先自己处理完,如果有后继责任链,则交给后面的责任链处理,递归下去T t = handWork(input);if (successor != null) {return successor.handWork(t);}return t;}/*** 自己的处理逻辑** @param intput* @return*/abstract T handWork(T intput);
}

对应的我们基于这个抽象类实现两个字符处理器,ProcessingStr1会将收到的中文逗号换位英文逗号:

public class ProcessingStr1 extends ProcessingObject<String> {@OverrideString handWork(String intput) {return intput.replace(",", ",");}
}

ProcessingStr2 会将中文句号替换为英文句号:

public class ProcessingStr2 extends ProcessingObject<String> {@OverrideString handWork(String intput) {return intput.replace("。", ".");}
}

测试代码和输出结果如下:

public static void main(String[] args) {ProcessingObject<String> p1 = new ProcessingStr1();ProcessingObject<String> p2 = new ProcessingStr2();p1.setSuccessor(p2);System.out.println(p1.handle("hello,world。"));}

可以看到所有的中文符号都被替换成英文符号了:

hello,world.

话不多说,不难看出上文这种传入String返回String的方法,我们完全可以使用UnaryOperator函数式接口实现表达式。

UnaryOperator源码可知,它继承Function,我们只需传入泛型T即可得到一个Function<T, T>,从而让我们得到一个T->TFunction表达式:

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {/*** Returns a unary operator that always returns its input argument.** @param <T> the type of the input and output of the operator* @return a unary operator that always returns its input argument*/static <T> UnaryOperator<T> identity() {return t -> t;}
}

而责任连的方式也很简单,因为UnaryOperator是Function的子类,这意味着我们可以使用FunctionandThen将所有的UnaryOperator完成衔接:

 UnaryOperator<String> p1 = i -> i.replace(",", ",");UnaryOperator<String> p2 = i -> i.replace("。", ".");p1.andThen(p2);System.out.println(p1.apply("hello,world。"));

小结

为了适配设计模式常会出现为了一段简单的逻辑,而去编写大量实现类的情况,所以我们建议,对于逻辑比较简单且需要适配设计模式的功能,可以尝试找到合适的函数式接口简化功能的实现,避免大量类文件的声明。

参考

Java 8 in Action

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/211327.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WorkPlus高效助力企业沟通的专业级即时通讯软件

在当今高度信息化和全球化竞争的世界&#xff0c;企业需要一个高效便捷的沟通工具来促进团队协作、提高工作效率。在这样的背景下&#xff0c;WorkPlus作为一款专业级的即时通讯软件应运而生。让我们一起深入了解WorkPlus&#xff0c;探讨其在企业沟通中的领先优势和卓越能力。…

平衡二叉树

AVL简称平衡二叉树&#xff0c;缩写为BBST&#xff0c;由苏联数学家 Adelse-Velskil 和 Landis 在 1962 年提出。 二叉树是动态查找的典范&#xff0c;但在极限情况下&#xff0c;二叉树的查找效果等同于链表&#xff0c;而平衡二叉树可以完美的达到 log ⁡ 2 n \log_2 n log2…

ElementPlus table 中嵌套 input 输入框

文章目录 需求分析 需求 vue3 项目中 使用UI组件库 ElementPlus 时&#xff0c;table 中嵌入 input输入框 分析 <template><div class"p-10"><el-table :data"tableData" border><el-table-column prop"date" label&qu…

课堂练习4.1:段式内存管理

4-1 课堂练习4.1&#xff1a;段式内存管理 段式内存管理以段为单位分配内存空间&#xff0c;段内连续&#xff0c;段间可以不连续。段可以很大&#xff0c;比如数据段、代码段、栈段等。本实训分析 Linux 0.11 的段式内存管理技术。 第1关1 号进程 mynext 变量的逻辑地址与线性…

cache教程 3.HTTP服务器

上一节我们实现了单机版的缓存服务&#xff0c;但是我们的目标是分布式缓存。那么&#xff0c;我们就需要把缓存服务部署到多态机器节点上&#xff0c;对外提供访问接口。客户端就可以通过这些接口去实现缓存的增删改查。 分布式缓存需要实现节点间通信&#xff0c;而通信方法…

【面试经典150 | 二叉树】翻转二叉树

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;递归方法二&#xff1a;迭代 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题…

4-SpringMVC

文章目录 项目源码地址回顾-MVC什么是MVC&#xff1f;MVC各部分组成 回顾-ServletMaven创建Web项目1、创建Maven父工程pom&#xff0c;并导入依赖2、用Maven新建一个Web Module3、代码&#xff1a;HelloServlet.java3、代码-hello.jsp3、代码-web.xml4、配置Tomcat5、浏览器测试…

github使用方法【附安装包】

如果你是一枚Coder&#xff0c;但是你不知道Github&#xff0c;那么我觉的你就不是一个菜鸟级别的Coder&#xff0c;因为你压根不是真正Coder&#xff0c;你只是一个Code搬运工。说明你根本不善于突破自己&#xff01;为什么这么说原因很简单&#xff0c;很多优秀的代码以及各种…

高级系统架构设计师之路

前言&#xff1a;系 统 架 构 设 计 师 (System Architecture Designer)是项目开发活动中的众多角色之 一 &#xff0c;它可 以是 一个人或 一个小组&#xff0c;也可以是一个团队。架构师 (Architect) 包含建筑师、设计师、创造 者、缔造者等含义&#xff0c;可以说&#xff0…

边缘计算系统设计与实践:引领科技创新的新浪潮

文章目录 一、边缘计算的概念二、边缘计算的设计原则三、边缘计算的关键技术四、边缘计算的实践应用《边缘计算系统设计与实践》特色内容简介作者简介目录前言/序言本书读者对象获取方式 随着物联网、大数据和人工智能等技术的快速发展&#xff0c;传统的中心化计算模式已经无法…

基于ssm人力资源管理系统论文

摘 要 随着企业员工人数的不断增多&#xff0c;企业在人力资源管理方面负担越来越重&#xff0c;因此&#xff0c;为提高企业人力资源管理效率&#xff0c;特开发了本人力资源管理系统。 本文重点阐述了人力资源管理系统的开发过程&#xff0c;以实际运用为开发背景&#xff0…

【大数据】Hudi 核心知识点详解(一)

Hudi 核心知识点详解&#xff08;一&#xff09; 1.数据湖与数据仓库的区别 &#xff1f;1.1 数据仓库1.2 数据湖1.3 两者的区别 2.Hudi 基础功能2.1 Hudi 简介2.2 Hudi 功能2.3 Hudi 的特性2.4 Hudi 的架构2.5 湖仓一体架构 3.Hudi 数据管理3.1 Hudi 表数据结构3.1.1 .hoodie …

【C语言】位运算实现二进制数据处理及BCD码转换

文章目录 1&#xff0e;编程实验&#xff1a;按short和unsigned short类型分别对-12345进行左移2位和右移2位操作&#xff0c;并输出结果。2&#xff0e;编程实验&#xff1a;利用位运算实现BCD码与十进制数之间的转换&#xff0c;假设数据类型为unsigned char。3&#xff0e;编…

多线程(进阶二:CAS)

目录 一、CAS的简单介绍 CAS逻辑&#xff08;用伪代码来描述&#xff09; 二、CAS在多线程中简单的使用 三、原子类自增的代码分析 都看到这了&#xff0c;点个赞再走吧&#xff0c;谢谢谢谢谢 一、CAS的简单介绍 CAS的全称&#xff1a;“Compare And Swap”&#xff0c;字…

C语言——字符函数和字符串函数(一)

&#x1f4dd;前言&#xff1a; 这篇文章对我最近学习的有关字符串的函数做一个总结和整理&#xff0c;主要讲解字符函数和字符串函数&#xff08;strlen&#xff0c;strcpy和strncpy&#xff0c;strcat和strncat&#xff09;的使用方法&#xff0c;使用场景和一些注意事项&…

Java入门项目--蚂蚁爱购

简介 这是一个靠谱的Java入门项目实战&#xff0c;名字叫蚂蚁爱购。 从零开发项目&#xff0c;视频加文档&#xff0c;十天就能学会开发JavaWeb项目&#xff0c;教程路线是&#xff1a;搭建环境> 安装软件> 创建项目> 添加依赖和配置> 通过表生成代码> 编写Ja…

解锁MySQL的威力:针对常见问题的快速解决指南

数据库和表的创建 创建数据库&#xff1a; CREATE DATABASE IF NOT EXISTS MyDatabase; USE MyDatabase;案例&#xff1a; 想象您要开始一个博客项目。首先&#xff0c;您需要一个地方来存储所有的文章和用户信息。上述命令帮助您创建了这样一个存储空间&#xff0c;名为MyDa…

RocketMQ-源码架构二

梳理一些比较完整&#xff0c;比较复杂的业务线 消息持久化设计 RocketMQ的持久化文件结构 消息持久化也就是将内存中的消息写入到本地磁盘的过程。而磁盘IO操作通常是一个很耗性能&#xff0c;很慢的操作&#xff0c;所以&#xff0c;对消息持久化机制的设计&#xff0c;是…

蒙特霍尔问题(选择三扇门后的车与羊)及其贝叶斯定理数学解释

1. 蒙特霍尔问题 有一个美国电视游戏节目叫做“Let’s Make a Deal”&#xff0c;游戏中参赛者将面对3扇关闭的门&#xff0c;其中一扇门背后有一辆汽车&#xff0c;另外两扇门后是山羊&#xff0c;参赛者如果能猜中哪一扇门后是汽车&#xff0c;就可以得到它。 通常&#xf…

javaee实验:文件上传及拦截器的使用

目录 文件上传ModelAttribute注解实验目的实验内容实验过程项目结构编写代码结果展示 文件上传 Spring MVC 提供 MultipartFile 接口作为参数来处理文件上传。 MultipartFile 提供以下方法来获取上传的文件信息&#xff1a;  getOriginalFilename 获取上传的文件名字&#x…