写点什么

用现代 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:559018

评论

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

解密领域驱动设计(DDD):搭建强大、灵活的软件架构神器

xfgg

Java 架构 DDD 领域驱动模型

阿里大佬耗时半年!肝出了这1015页分布式全栈手册

程序知音

Java 分布式 java架构 Java进阶 后端技术

免费堡垒机选择云堡垒机可以吗?哪家好?

行云管家

堡垒机 云堡垒机 免费堡垒机

开源之夏 2023|欢迎报名 Apache RocketMQ 社区项目!

Apache RocketMQ

RocketMQ 开源之夏

vue组件通信方式有哪些?

bb_xiaxia1998

Vue 前端

美团前端vue面试题

bb_xiaxia1998

Vue 前端

使用 NFTScan NFT API 开发一个多链 NFT Marketplace

NFT Research

API NFT\

行云流水| CI 3.0 云原生构建全新上线

CODING DevOps

DevOps 云原生 软件工程 研发效能 持续构建

万物可卷!低代码充满想象,能打敢战

引迈信息

低代码 JNPF

天翼云CDN全站加速产品对websocket协议的支持

天翼云开发者社区

云计算 CDN

太爆了!阿里最新出品2023版JDK源码学习指南,Github三天已万赞

程序员小毕

程序员 后端 jdk源码 架构师 java面试

大数据如何助力营销(4)策略制定

MobTech袤博科技

使用数据可视化分析还有多少人在玩梦幻西游

搞大屏的小北

数据可视化 用户画像 梦幻西游 游戏分析

聊聊原生拖拽API

巨梦科技

django Vue

豆浆、油条、肉夹馍......西安银行的挑战开始了

OceanBase 数据库

数据库 oceanbase

Pose泰裤辣! 一键提取姿态生成新图像

华为云开发者联盟

人工智能 AI 华为云 华为云开发者联盟 企业号 5 月 PK 榜

靠这份GitHub 标星80K的图解算法,杀进大厂!

程序知音

Java 数据结构 算法 后端技术 算法与数据结构

使用MFT进行加密文件传输的7个好处

镭速

数据可视化图表之雷达图介绍

2D3D前端可视化开发

数据分析 数据可视化 数据可视化工具 可视化图表 雷达图

学术加油站|基于LSM-tree存储系统的内存管理,最大限度降低I/O成本

OceanBase 数据库

数据库 oceanbase

滴滴前端必会vue面试题汇总

bb_xiaxia1998

Vue 前端

vue组件通信6种方式总结(常问知识点)

bb_xiaxia1998

Vue 前端

ChatGPT与深度学习的完美融合:打造智能化推荐系统新时代

GPU算力

虚拟化技术 - CPU虚拟化

天翼云开发者社区

cpu 虚拟化

亚马逊云是哪个国家的?收费标准贵吗?

行云管家

云计算 云服务 云管理 亚马逊云

全是技巧!ZBrush雕刻手部教程赶紧收藏!

Finovy Cloud

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