2月5-7日QCon全球软件开发大会携手100+位大咖讲师落定北京,点击查看完整日程>> 了解详情
写点什么

用现代 Java 调整经典设计模式

  • 2022-10-28
    北京
  • 本文字数:2955 字

    阅读完需:约 10 分钟

用现代Java调整经典设计模式

1998 年出版的《设计模式——可复用面向对象软件的基础》有资格成为计算机科学的经典著作,大学仍然将它作为教材,并被奉为业界的最佳实践。在 Devoxx 的一场深度讨论中,Venkat Subramaniam使用现代 Java 实现了迭代器策略装饰器工厂方法模式,对原有的设计模式进行了一番调整。


讨论的引言部分,Subramaniam 表示这本书的作者是软件开发的祖父,他们的设计模式是祖母的食谱——但即使你有了这些食谱,也不一定能做出这些菜。因此,他认为将设计模式作为一种沟通工具是有意义的,但将其作为一种软件设计工具却是一场灾难。


以下是我们在日常编程中可能遇到的常见模式,Subramaniam 通过充满活力和快乐的方式让这些模式变得更加流式。


由于 Java 加入了函数式编程,迭代器模式发生了很大的变化。最大的一个变化是从外部迭代器到内部迭代器的转变,这是 Java 函数式 API 带来的。这个变化可以让你从使用冗长的命令式迭代


int count = 0;for(var name: names) {   if(name.length() == 4) {     System.out.println(name.toUpperCase());   count++;
if(count == 2) { break; } } }}
复制代码


演变成使用流式的函数式迭代


names.stream()     .filter(name -> name.length() == 4)     .map(String::toUpperCase)     .limit(2)     .forEach(System.out::println);
复制代码


limit(long)takeWhile(Predicate<? super T>)(在 Java 9 中添加的)是continuebreak语句的等效函数,第一个只接受数值限制参数,而第二个可以接受表达式。


尽管 Java 的函数式 API 作为 JDK 的一部分已经有近十年的时间了,但在代码库中仍然存在一些常见的错误。当函数管道“不”纯粹(修改或依赖外部可见的状态)时,可能会导致迭代操作的结果不可预测(特别是在进行并行执行时)。


策略模式——我们希望改变算法的一小部分,同时保持算法的其余部分不变。从历史上看,这个模式是通过一个方法来实现的,这个方法采用一个方法接口作为参数,作为参数的方法接口可以有多个策略实现,一个策略通常就是一个方法或函数。因此,函数式接口和 lambda 表达式在这里很适用。


虽然匿名类可以作为一种实现机制,但函数接口(Predicate<? super T>是一个很好的选择)或 lambda 表达式让代码变得更加流式,更容易理解。在现代 Java 中,策略模式更多的是一种特性,而不是需要付出大量努力才能实现的模式。


public class Sample {  public static int totalValues(List<Integer> numbers) {    int total = 0;
for(var number: numbers) { total += number; }
return total; }
public static int totalEvenValues(List<Integer> numbers) { int total = 0;
for(var number: numbers) { if(number % 2 == 0) { total += number; } }
return total; }
public static int totalOddValues(List<Integer> numbers) { int total = 0;
for(var number: numbers) { if(number % 2 != 0) { total += number; } }
return total; }
public static void main(String[] args) { var numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(totalValues(numbers)); System.out.println(totalEvenValues(numbers)); System.out.println(totalOddValues(numbers)); }}
复制代码


现代的做法是使用 lambda 表达式来表示策略。


import java.util.function.Predicate;
public class Sample { public static int totalValues(List<Integer> numbers, Predicate<Integer> selector) { int total = 0;
for(var number: numbers) { if(selector.test(number)) { total += number; } }
return total; }
public static boolean isOdd(int number) { return number % 2 != 0; }
public static void main(String[] args) { var numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(totalValues(numbers, ignore -> true)); System.out.println(totalValues(numbers, number -> number % 2 == 0));
System.out.println(totalValues(numbers, Sample::isOdd)); }}
复制代码


在介绍工厂方法实现时,Venkat 陈述了以下内容。


从多态的角度来看,Java 中最糟糕的关键字是什么?尽管 final、instanceof 和 static 都可能算是最糟糕的关键字,但它们都只是小兵,new才是它们当中的黑手党。多模式(创建模式)、框架(SpringGuice)是为了解决 new 的“弊端”——缺乏多态性支持和紧密耦合。受Ruby基于上下文创建不同对象的多态能力的启发,Venkat 使用 Java 的default关键字实现工厂方法模式。这种方法使用了接口和非常小的实现类,让代码变得更容易理解。


import java.util.*;
interface Pet {}class Dog implements Pet {}class Cat implements Pet {}
interface Person { Pet getPet();
default void play() { System.out.println("playing with " + getPet()); }}
class DogPerson implements Person { private Dog dog = new Dog();
public Pet getPet() { return dog; }}
class CatLover implements Person { private Cat cat = new Cat(); public Pet getPet() { return cat; }}
public class Sample { public static void call(Person person) { person.play(); }
public static void main(String[] args) { call(new DogPerson()); call(new CatLover()); }}
复制代码


即使装饰器模式在理论上为许多程序员所熟知,但实际上很少有人使用它。它的实现最臭名昭著的例子可能是 io 包。Venkat 基于函数的可组合性提出了一种不同的方法——使用identity函数和andThen(Function<? super R,? extends V>)构建简单、流式的机制来增强对象的能力。


class Camera {  private Function<Color, Color> filter;
public Camera(Function<Color, Color>... filters) { filter = Stream.of(filters) .reduce(Function.identity(), Function::andThen); }
public Color snap(Color input) { return filter.apply(input); }}
public class Sample { public static void print(Camera camera) { System.out.println(camera.snap(new Color(125, 125, 125))); }
public static void main(String[] args) { print(new Camera());
print(new Camera(Color::brighter)); print(new Camera(Color::darker));
print(new Camera(Color::brighter, Color::darker)); }}
复制代码


即使模式看起来会一直存在,就像 Subramaniam在讨论中提到的:“设计模式经常被用来填补编程语言的空白。一门语言越强大,我们就越少谈论设计模式,因为设计模式自然会成为语言的特性。”


随着编程语言的演进和我们经验的积累,模式也会随着时间的推移而演变。其中一些模式被吸收为语言的特性,另一些则被认为已过时,而另一些变得更加容易实现。不管你最喜欢的是哪一类,Venkat 建议把它们作为交流的手段,并让代码朝着这些模式的方向演变。此外,他建议尝试使用多种编程语言,让代码变得更加流式。


原文链接

https://www.infoq.com/news/2022/10/modern-java-design-patterns/

2022-10-28 09:557866

评论

发布
暂无评论
发现更多内容

揭秘在召唤师峡谷中移动路径选择逻辑?

华为云开发者联盟

算法 地图 最短路径

Apache DolphinScheduler 是如何走进Apache的

代立冬

大数据 数据湖调度 DolphinScheduler Apache DolphinScheduler

架构训练营 - 第7周课后作业 - 学习总结

Pudding

如何稳扎稳打推进数字货币进程

CECBC

数字货币

从智慧计算的点、线、面,读懂浪潮AI的进化轨迹

脑极体

害怕重构?都怪我太晚和你介绍该如何重构,现在我来了

小Q

Java 学习 程序员 面试 重构

医疗界“最强大脑”落户杭州!阿里巴巴联合浙大一院共同打造

互联网

《Python:Python编程简介:计算机编程和机器学习入门指南》

计算机与AI

Python

百亿级数据分表后怎么分页查询?

艾小仙

Java MySQL 数据库 编程语言 分库分表

数字货币OTC交易所开发,交易所搭建方案

13530558032

如何实现后台管理系统的权限路由和权限菜单

徐小夕

Java 大前端 编辑器 H5 数据可视化

谈谈敏捷开发概念和迭代开发方案

Learun

敏捷开发

帮助企业摆脱困境,名企归乡工程师:能成功全靠有它!

Philips

敏捷开发

LeetCode题解:77. 组合,递归回溯,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

架构师训练营第 1 期第 7 周总结

du tiezheng

极客大学架构师训练营

价值超10亿美元的直播系统架构图是什么样子的?

冰河

系统架构 高并发 高性能 亿级流量 直播架构

CDN是什么?

德胜网络-阳

USDT承兑支付平台技术开发,承兑商币支付交易平台搭建

13530558032

从一场“众盟科技云滇之播”,我们发现了美食直播的商业与公益价值

脑极体

浅谈API网关(API Gateway)如何承载API经济生态链

华为云开发者联盟

API 网关

【涂鸦物联网足迹】涂鸦云平台接口说明

IoT云工坊

人工智能 物联网 API sdk 云平台

又一道比较运算符相关的面试题让我明白基础很重要

Gopher指北

Go 语言

爆料!前华为微服务专家纯手打500页落地架构实战笔记,已开源

996小迁

架构 面试 分布式 微服务 程序人生

魏际刚:精准谋划我国供应链发展新方位

CECBC

供应链 物流

金融科技的未来

CECBC

金融

做个别人家的网页

MySQL从删库到跑路

html/css 网页设计

架构师训练营第一期 - week8

习习

终于啃完了Java核心原理+框架“面试圣经”成功五面上岸美团

小Q

Java 学习 编程 架构 面试

一款区块链钱包开发需要多少钱?数字资产钱包开发搭建

13530558032

【运维思考】如何做好云上运维服务?

嘉为蓝鲸

云计算 运维 数字化转型 数据中心 云服务

架构师训练营 - 第 7 周课后作业(1 期)

Pudding

用现代Java调整经典设计模式_语言 & 开发_Olimpiu Pop_InfoQ精选文章