Java 设计模式最佳实践:1~5(3)

简介: Java 设计模式最佳实践:1~5(3)

Java 设计模式最佳实践:1~5(2)https://developer.aliyun.com/article/1426754

适配器

最好的例子是使用map函数,它执行从旧接口到新接口的自适应。我们将重用第 4 章中的示例“结构模式”,稍加改动;映射模拟适配器代码:

jshell> class PS2Device {};
| created class PS2Device
jshell> class USBDevice {};
| created class USBDevice
jshell> Optional.of(new PS2Device()).stream().map(x -> new USBDevice()).findFirst().get()
$39 ==> USBDevice@15bb6bea

装饰器

装饰器可以通过利用函数组合来实现。例如,如前所示,可以使用stream.peek方法将日志添加到现有函数调用,并从提供给peekConsumer将日志记录到控制台。

我们的第 4 章“结构模式”,装饰器示例可以用函数式重写;注意装饰器用于使用与初始装饰器消费者相同的输入:

jshell> Consumer<String> toASCII = x -> System.out.println("Print ASCII: " + x);
toASCII ==> $Lambda$159/1690859824@400cff1a
jshell> Function<String, String> toHex = x -> x.chars().boxed().map(y -> "0x" + Integer.toHexString(y)).collect(Collectors.joining(" "));
toHex ==> $Lambda$158/1860250540@55040f2f
jshell> Consumer<String> decorateToHex = x -> System.out.println("Print HEX: " + toHex.apply(x))
decorateToHex ==> $Lambda$160/1381965390@75f9eccc
jshell> toASCII.andThen(decorateToHex).accept("text")
Print ASCII: text
Print HEX: 0x74 0x65 0x78 0x74

责任链

责任链可以实现为处理器(函数)的列表,每个处理器执行一个特定的操作。下面的示例代码使用闭包和一系列函数,这些函数一个接一个地应用于给定的文本:

jshell> String text = "Text";
text ==> "Text"
jshell> Stream.<Function<String, String>>of(String::toLowerCase, x -> LocalDateTime.now().toString() + " " + x).map(f -> f.apply(text)).collect(Collectors.toList())
$55 ==> [text, 2017-08-10T08:41:28.243310800 Text]

命令

其目的是将一个方法转换成一个对象来存储它并在以后调用它,能够跟踪它的调用、记录和撤消。这是Consumer类的基本用法。

在下面的代码中,我们将创建一个命令列表并逐个执行它们:

jshell> List<Consumer<String>> tasks = List.of(System.out::println, x -> System.out.println(LocalDateTime.now().toString() + " " + x))
tasks ==> [$Lambda$192/728258269@6107227e, $Lambda$193/1572098393@7c417213]
jshell> tasks.forEach(x -> x.accept(text))
Text
2017-08-10T08:47:31.673812300 Text

解释器

解释器的语法可以存储为关键字映射,相应的操作存储为值。在第二章“创建模式”中,我们使用了一个数学表达式求值器,将结果累加成一个栈。这可以通过将表达式存储在映射中来实现,并使用reduce来累加结果:

jshell> Map<String, IntBinaryOperator> operands = Map.of("+", (x, y) -> x + y, "-", (x, y) -> x - y)
operands ==> {-=$Lambda$208/1259652483@65466a6a, +=$Lambda$207/1552978964@4ddced80}
jshell> Arrays.asList("4 5 + 6 -".split(" ")).stream().reduce("0 ",(acc, x) -> {
...> if (operands.containsKey(x)) {
...> String[] split = acc.split(" ");
...> System.out.println(acc);
...> acc = split[0] + " " + operands.get(x).applyAsInt(Integer.valueOf(split[1]), Integer.valueOf(split[2])) + " ";
...> } else { acc = acc + x + " ";}
...> return acc; }).split(" ")[1]
0 4 5
0 9 6
$76 ==> "3"

迭代器

迭代器部分是通过使用流提供的序列来实现的。Java8 添加了forEach方法,该方法接收消费者作为参数,其行为与前面的循环实现类似,如下面的示例代码所示:

jshell> List.of(1, 4).forEach(System.out::println)
jshell> for(Integer i: List.of(1, 4)) System.out.println(i);

如预期的那样,每个示例的输出是 1 和 4。

观察者

在 Java8 中,观察者模式被 Lambda 表达式取代。最明显的例子是ActionListener替换。使用匿名类监听器的旧代码被替换为一个简单的函数调用:

JButton button = new Jbutton("Click Here");
button.addActionListener(new ActionListener() 
{ 
  public void actionPerformed(ActionEvent e) 
  {
    System.out.println("Handled by the old listener");
  }
});

新代码只有一行:

button.addActionListener(e -> System.out.println("Handled by lambda"));

策略

这个策略可以被一个函数代替。在下面的代码示例中,我们对所有价格应用 10% 的折扣策略:

jshell> Function<Double, Double> tenPercentDiscount = x -> x * 0.9;
tenPercentDiscount ==> $Lambda$217/1990160809@4c9f8c13
jshell> List.<Double>of(5.4, 6.27, 3.29).stream().map(tenPercentDiscount).collect(Collectors.toList())
$98 ==> [4.86, 5.643, 2.9610000000000003]

模板方法

当模板提供调用顺序时,可以实现模板方法以允许注入特定的方法调用。在下面的示例中,我们将添加特定的调用并从外部设置它们的内容。它们可能已经插入了特定的内容。通过使用接收所有可运行项的单个方法,可以简化代码:

jshell> class TemplateMethod {
...> private Runnable call1 = () -> {};
...> private Runnable call2 = () -> System.out.println("Call2");
...> private Runnable call3 = () -> {};
...> public void setCall1(Runnable call1) { this.call1 = call1;}
...> public void setCall2(Runnable call2) { this.call2 = call2; }
...> public void setCall3(Runnable call3) { this.call3 = call3; }
...> public void run() {
...> call1.run();
...> call2.run();
...> call3.run();
...> }
...> }
| created class TemplateMethod
jshell> TemplateMethod t = new TemplateMethod();
t ==> TemplateMethod@70e8f8e
jshell> t.setCall1(() -> System.out.println("Call1"));
jshell> t.setCall3(() -> System.out.println("Call3"));
jshell> t.run();
Call1
Call2
Call3

函数式设计模式

在本节中,我们将学习以下函数式设计模式:

  • 映射和归约
  • 借贷模式
  • 尾部调用优化
  • 回忆录
  • 环绕执行方法

映射和归约

MapReduce 是 Google 开发的一种用于大规模并行编程的技术,由于易于表达,它以函数设计模式出现。在函数式编程中,它是单子的一种形式。

意图

其目的是将现有任务分解为多个较小的任务,并行运行它们,并聚合结果(reduce)。它有望提高大数据的性能。

示例

我们将通过基于给定的 Sleuth 跨度解析和聚合来自多个 Web 服务的日志并计算每个命中端点的总持续时间来演示 MapReduce 模式的用法。日志取自这个页面并拆分成相应的服务日志文件。下面的代码并行读取所有日志、映射、排序和过滤相关日志条目,收集并减少(聚合)结果。如果有结果,它将被打印到控制台。导入的日期/时间类用于排序比较。flatMap代码需要处理Exception,如下代码所示:

jshell> import java.time.*
jshell> import java.time.format.*
jshell> DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
dtf ==> Value(YearOfEra,4,19,EXCEEDS_PAD)'-'Value(MonthOf ... Fraction(NanoOfSecond,3,3)
jshell> try (Stream<Path> files = Files.find(Paths.get("d:/"), 1, (path, attr) -> String.valueOf(path).endsWith(".log"))) {
...> files.parallel().
...> flatMap(x -> { try { return Files.lines(x); } catch (IOException e) {} return null;}).
...> filter(x -> x.contains("2485ec27856c56f4")).
...> map(x -> x.substring(0, 23) + " " + x.split(":")[3]).
...> sorted((x, y) -> LocalDateTime.parse(x.substring(0, 23), dtf).compareTo(LocalDateTime.parse(y.substring(0, 23), dtf))).
...> collect(Collectors.toList()).stream().sequential().
...> reduce((acc, x) -> {
...> if (acc.length() > 0) {
...> Long duration = Long.valueOf(Duration.between(LocalDateTime.parse(acc.substring(0, 23), dtf), LocalDateTime.parse(x.substring(0, 23), dtf)).t oMillis());
...> acc += "n After " + duration.toString() + "ms " + x.substring(24);
...> } else {
...> acc = x;
...> }
...> return acc;}).ifPresent(System.out::println);
...> }
2016-02-26 11:15:47.561 Hello from service1\. Calling service2
After 149ms Hello from service2\. Calling service3 and then service4
After 334ms Hello from service3
After 363ms Got response from service3 [Hello from service3]
After 573ms Hello from service4
After 595ms Got response from service4 [Hello from service4]
After 621ms Got response from service2 [Hello from service2, response from service3 [Hello from service3] and from service4 [Hello from service4]]

借贷模式

借贷模式确保资源一旦超出范围就被决定性地处置。资源可以是数据库连接、文件、套接字或任何处理本机资源的对象(内存、系统句柄、任何类型的连接)之一。这与 MSDN 上描述的 Dispose 模式的意图类似。

意图

这样做的目的是让用户在未使用的资源被使用后,从释放这些资源的负担中解脱出来。用户可能忘记调用资源的release方法,从而导致泄漏。

示例

在处理数据库事务时,最常用的模板之一是获取事务、进行适当的调用、确保在异常时提交或回滚并关闭事务。这可以实现为借贷模式,其中移动部分是事务中的调用。以下代码显示了如何实现这一点:

jshell> class Connection {
...> public void commit() {};
public void rollback() {};
public void close() {};
public void setAutoCommit(boolean autoCommit) {};
...> public static void runWithinTransaction(Consumer<Connection> c) {
...> Connection t = null;
...> try { t = new Connection(); t.setAutoCommit(false);
...> c.accept(t);
...> t.commit();
...> } catch(Exception e) { t.rollback(); } finally { t.close(); } } }
| created class Connection
jshell> Connection.runWithinTransaction(x -> System.out.println("Execute statement..."));
Execute statement...

Java 设计模式最佳实践:1~5(4)https://developer.aliyun.com/article/1426756

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
13小时前
|
设计模式 前端开发 Java
19:Web开发模式与MVC设计模式-Java Web
19:Web开发模式与MVC设计模式-Java Web
9 4
|
13小时前
|
设计模式 存储 前端开发
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
11 4
|
17小时前
|
设计模式 缓存 监控
JAVA设计模式之结构型模式
结构模型:适配器模型、桥接模型、过滤器模型、组合模型、装饰器模型、外观模型、享受元模型和代理模型。
10 3
|
4天前
|
设计模式 算法 Java
Java基础教程(19)-设计模式简述
【4月更文挑战第19天】设计模式是软件设计中反复使用的代码设计经验,旨在提升代码的可重用性、可扩展性和可维护性。23种模式分为创建型、结构型和行为型三类。创建型模式如工厂方法、抽象工厂、建造者、原型和单例,关注对象创建与使用的分离。结构型模式涉及对象组合,如适配器、装饰器、外观等,增强结构灵活性。行为型模式专注于对象间职责分配和算法合作,包括责任链、命令、观察者等。设计模式提供标准化解决方案,促进代码交流和复用。
|
5天前
|
设计模式 Java
Java 设计模式:混合、装饰器与组合的编程实践
【4月更文挑战第27天】在面向对象编程中,混合(Mixins)、装饰器(Decorators)和组合(Composition)是三种强大的设计模式,用于增强和扩展类的功能。
11 1
|
5天前
|
设计模式 消息中间件 Java
Java 设计模式:探索发布-订阅模式的原理与应用
【4月更文挑战第27天】发布-订阅模式是一种消息传递范式,被广泛用于构建松散耦合的系统。在 Java 中,这种模式允许多个对象监听和响应感兴趣的事件。
21 2
|
5天前
|
设计模式 算法 Java
Java 设计模式:深入模板方法模式的原理与应用
【4月更文挑战第27天】模板方法模式是一种行为设计模式,主要用于定义一个操作中的算法的框架,允许子类在不改变算法结构的情况下重定义算法的某些特定步骤。
14 1
|
5天前
|
设计模式 算法 Java
Java 设计模式:探索策略模式的概念和实战应用
【4月更文挑战第27天】策略模式是一种行为设计模式,它允许在运行时选择算法的行为。在 Java 中,策略模式通过定义一系列的算法,并将每一个算法封装起来,并使它们可以互换,这样算法的变化不会影响到使用算法的客户。
11 1
|
5天前
|
设计模式 存储 安全
Java 设计模式:深入单例模式的理解与应用
【4月更文挑战第27天】单例模式是一种常用的设计模式,在 Java 开发中扮演着重要角色。此模式的主要目的是保证一个类只有一个实例,并提供一个全局访问点。
12 0
|
5天前
|
设计模式 Java
Java 设计模式:工厂模式与抽象工厂模式的解析与应用
【4月更文挑战第27天】设计模式是软件开发中用于解决常见问题的典型解决方案。在 Java 中,工厂模式和抽象工厂模式是创建型模式中非常核心的模式,它们主要用于对象的创建,有助于增加程序的灵活性和扩展性。本博客将详细介绍这两种模式的概念、区别以及如何在实际项目中应用这些模式。
10 1