【函数式接口使用✈️✈️】通过具体的例子实现函数结合策略模式的使用

news2024/11/29 12:39:33

目录

前言

一、核心函数式接口

1. Consumer

2.  Supplier

3.  Function,>

二、场景模拟

         1.面向对象设计

2. 策略接口实现(以 Function 接口作为策略) 

三、对比


前言

        在 Java 8 中引入了Stream API 新特性,这使得函数式编程风格进一步得到巩固,其中伴随着Lambda 表达式和 Stream API 的广泛使用,另一种函数式接口风格亦可以简化代码提升可读性和拓展性,具体如下

一、核心函数式接口

1. Consumer<T>

  • 定义了一个接受单一输入参数并且无返回值的操作。常用于数据处理流程中的消费型操作,如打印日志、更新数据库等。
  •         List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
            names.forEach(e-> System.out.println("Welcome login : "+e));
            // 这里是使用的Consumer<String>,给一个参数执行相关操作
    
            // 或者定义一个自定义Consumer
            Consumer<String> logAction = name -> System.out.println("Logging action for: " + name);
            names.forEach(logAction);

2.  Supplier<T>

  • 定义了一个不接受任何参数但是会产生一个结果的方法引用。常用于提供数据来源或计算某个值。
  •         Supplier<Integer> randomIntSupplier = () -> ThreadLocalRandom.current().nextInt(1, 100);
            System.out.println(randomIntSupplier.get()); // 输出一个1到100之间的随机整数

3.  Function<T, R>

  • 定义了一个接受一个输入参数并产生一个输出结果的方法引用。常用于数据转换、映射或计算。
  •         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
            List<Double> doubles = numbers.stream().map((Function<Integer, Double>)                                 
            Integer::doubleValue).collect(Collectors.toList());
            doubles.forEach(System.out::println);
    
            // 或者自定义Function
            Function<String, String> upperCaseTransformer = String::toUpperCase;
            String transformed = upperCaseTransformer.apply("hello"); // 输出 "HELLO"
            System.out.println(transformed);

二、场景模拟

         1.面向对象设计

        比如常见的促销活动中,不同的促销策略计算出商品的最终价格是不一样的,采用传统的面向对象设计的话,需要为每一个促销活动创建独立的方法或者类了,并在购物车类中通过直接调用相应的方法计算,如下:

public class ShoppingCart {
    //购物车中的商品列表
    private List<Product> products;

    //普通不打折,统计所有商品的价格即可
    public double calculateTotalPriceWithNormalPrice() {
        double totalPrice = products.stream()
                                    .map(Product::getPrice)
                                    .reduce(0.0, Double::sum);
        return totalPrice;
    }


    //促销打九折,统计商品价格的九折
    public double calculateTotalPriceWithTenPercentDiscount() {
        double totalPrice = products.stream()
                                    .map(product -> product.getPrice() * 0.9)
                                    .reduce(0.0, Double::sum);
        return totalPrice;
    }

    
    //促销直减50 ,小于0 的按照0元计算
    public double calculateTotalPriceWithFiftyDollarsOff() {
        double totalPrice = products.stream()
                                    .map(product -> Math.max(product.getPrice() - 50.0, 0.0))
                                    .reduce(0.0, Double::sum);
        return totalPrice;
    }

    // 调用示例
    public void processCheckout(CheckoutType type) {
        switch (type) {
            case NORMAL_PRICE:
                double normalPrice = calculateTotalPriceWithNormalPrice();
                // 处理正常价格结算逻辑
                break;
            case TEN_PERCENT_DISCOUNT:
                double tenPercentDiscount = calculateTotalPriceWithTenPercentDiscount();
                // 处理九折结算逻辑
                break;
            case FIFTY_DOLLARS_OFF:
                double fiftyDollarsOff = calculateTotalPriceWithFiftyDollarsOff();
                // 处理直减50美元结算逻辑
                break;
        }
    }

    // 其他方法...
}

enum CheckoutType {
    NORMAL_PRICE,
    TEN_PERCENT_DISCOUNT,
    FIFTY_DOLLARS_OFF
}

        这种方式增加了代码的耦合度,并且如果需要新增或者修改促销策略,就需要修改ShoppingCart类

2. 策略接口实现(以 Function 接口作为策略) 

import java.util.function.Function;

public interface PromotionStrategy extends Function<Double, Double> {
    // 不需要额外的方法,因为Function本身就是一种策略(接受一个参数,返回一个结果),它接受原始价格并返回打折后的价格
}

        创建几个具体的策略实现

public class NormalPriceStrategy implements PromotionStrategy {
    @Override
    public Double apply(Double originalPrice) {
        return originalPrice; // 正常价格,不做打折处理
    }
}

public class TenPercentDiscountStrategy implements PromotionStrategy {
    @Override
    public Double apply(Double originalPrice) {
        return originalPrice * 0.9; // 打九折
    }
}

public class FiftyDollarsOffStrategy implements PromotionStrategy {
    @Override
    public Double apply(Double originalPrice) {
        return Math.max(originalPrice - 50.0, 0.0); // 直减50美元,价格不能低于0
    }
}

            之后,在购物车计算逻辑中,可以根据用户选择的促销策略动态计算商品的价格:

public class ShoppingCart {
    private List<Product> products;
    private PromotionStrategy promotionStrategy;

    public ShoppingCart(PromotionStrategy strategy) {
        this.promotionStrategy = strategy;
        // 初始化产品列表...
    }

    public double calculateTotalPrice() {
        double totalPrice = products.stream()
                                    .map(Product::getPrice)
                                    .map(promotionStrategy)
                                    .reduce(0.0, Double::sum);
        return totalPrice;
    }

    // 其他方法...
}

// 使用示例:
ShoppingCart cart = new ShoppingCart(new TenPercentDiscountStrategy());
// 添加商品到cart...
double finalPrice = cart.calculateTotalPrice(); // 根据策略计算总价

        这个例子就是使用 PromotionStrategy 扮演了策略角色,不同的折扣策略通过实现 Function<Double,Double> 接口来决定如何计算折扣价,在使用时,可以根据需要选择并注入不同的策略实现。

三、对比

策略模式面向对象设计
优点
  • 开放封闭原则:策略模式鼓励对扩展开放,对修改封闭。当需要增加新的促销策略时,只需要增加一个新的策略类,不需要修改现有的购物车类或者其他已有代码。
  • 代码复用:每个策略类(如NormalPriceStrategyTenPercentDiscountStrategyFiftyDollarsOffStrategy)可以独立于购物车类使用,增强了代码的复用性。
  • 低耦合:购物车类与具体的促销策略解耦,使得系统更灵活,更容易维护。
  • 对于简单的场景,直接在购物车类中添加多个计算方法直观易懂,初学者更容易接受。
缺点
  • 策略种类增多时,可能会导致策略接口的家族变得庞大,若策略逻辑差异不大,可能会造成代码冗余。
  • 耦合度高:购物车类与具体的促销逻辑紧密耦合,当促销策略发生变化时,必须修改购物车类的代码。
  • 扩展困难:若促销策略种类增加,会导致购物车类的代码臃肿,且不利于代码维护。
  • 代码复用性差:对于每一种新的促销活动,都需要在购物车类中添加新的方法,无法直接复用现有逻辑。

        其实不难看出,在面对频繁变化的业务逻辑(如促销策略)时,策略模式的优势明显,它有助于代码的可维护性、扩展性和复用性。而在简单、固定的场景下,直接在购物车类中硬编码计算逻辑可能显得更为直接简单。然而,考虑到长期的软件迭代和维护成本,推荐采用策略模式来优化代码结构。

文末

        文章到这里就结束了~

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

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

相关文章

全国产化无风扇嵌入式车载电脑在救护车远端诊断的行业应用

救护车远端诊断的行业应用 背景介绍 更加快速的为急症病人在第一时间开始进行诊断和治疗,是提高病人救助成功率的关键。因此&#xff0c;先进的救护系统正在思考&#xff0c;如何在病人进入救护车之后&#xff0c;立刻能够将救护车中各种检查仪器的信息快速的传回医院&#xf…

学习大数据的第一天

今天学习如何安装hapood安装 1.安装hapood安装 2.需要的资料 3.开始安装 1.创建目录 mkdir -p /export/server 2.进入目录下 cd /export/server/ 3.安装 安装需要的依赖 yum install gcc gcc-c make autoconf automake libtool curl lzo-devel zlib-devel openssl opens…

Redux极客园项目初始化搭建

基本结构搭建 实现步骤 在 Login/index.js 中创建登录页面基本结构在 Login 目录中创建 index.scss 文件&#xff0c;指定组件样式将 logo.png 和 login.png 拷贝到 assets 目录中 代码实现 pages/Login/index.js import ./index.scss import { Card, Form, Input, Button }…

CSS3 新特性 box-shadow 阴影效果、圆角border-radius

圆角 使用CSS3 border-radius属性&#xff0c;你可以给任何元素制作"圆角"&#xff0c;border-radius属性&#xff0c;可以使用以下规则&#xff1a; &#xff08;1&#xff09;四个值&#xff1a;第一个值为左上角&#xff0c;第二个值为右上角&#xff0c;第三个值…

CST电磁仿真物体表面的Sheet结构和生成3D Model【基础教程】

由Sheet结构生成3D Model 使用Shell Solid and Thicken Sheet&#xff01; Modeling > Tools > Shape Tools > Shell Solid or Thicken Sheet Shell Solidor ThickenSheet会根据不同类型的模型提供两种完全不同的功能。 如033.由3D Model生成Cavity 所述&#xff…

04-15 周一 GitHub仓库CI服务器actions-runner和workflow yaml配置文档解析

04-15 周一 GitHub仓库CI服务器配置过程文档 时间版本修改人描述2024年4月15日10:35:52V0.1宋全恒新建文档2024年4月17日10:33:20v1.0宋全恒完成github actions CI的配置和工作流配置文件解读文档的撰写 简介 一些基础概念 前提知识 仓库介绍 地址镜像介绍https://github.…

Mac电脑上有什么好玩的格斗游戏 《真人快打1》可以在苹果电脑上玩吗

你是不是喜欢玩格斗游戏&#xff1f;你是不是想在你的Mac电脑上体验一些刺激和激烈的对战&#xff1f;在这篇文章中&#xff0c;我们将介绍Mac电脑上有什么好玩的格斗游戏&#xff0c;以及《真人快打1》可以在苹果电脑上玩吗。 一、Mac电脑上有什么好玩的格斗游戏 格斗游戏是…

使用prompt_toolkit构建交互式命令行工具

prompt_toolkit是一个python库&#xff0c;用于构建命令行工具和终端应用。其官网介绍如下&#xff0c; prompt_toolkit is a library for building powerful interactive command line and terminal applications in Python. 安装命令如下&#xff0c; pip install prompt_to…

2024Guitar Pro 8.1 Mac 最新下载、安装、激活、换机图文教程

吉他爱好者必备神器&#xff1a;Guitar Pro v8.1.1 Build 17深度解析 随着数字音乐制作和学习的日益普及&#xff0c;越来越多的吉他爱好者开始寻找能够帮助他们提升技能、创作音乐的专业工具。在众多吉他制作软件中&#xff0c;Guitar Pro因其强大的功能和易用的界面备受推崇…

03-JAVA设计模式-中介者模式

中介者模式 什么是中介者模式 中介者模式&#xff08;Mediator Pattern&#xff09;是一种行为设计模式&#xff0c;用于减少对象之间的直接依赖关系&#xff0c;降低它们之间的耦合度&#xff0c;并使得一个对象改变时&#xff0c;所有依赖于它的对象都得到通知并自动更新。…

【QT学习】7.事件,把文本显示在页面中(文本可变),鼠标指针切换,鼠标左键右键按下,qt设置背景样式

0.创建项目&#xff0c;事件的创建 1.事件的位置 2.这就是多态&#xff0c;子类重写父类函数&#xff0c;子类调用子类函数&#xff0c;也可以调用父类函数。但同函数名 1.要求&#xff1a;文本显示在页面中&#xff08;文本可变&#xff09; 1.文本显示在页面的核心代码 主要步…

链表OJ - 5(合并两个有序链表)

题目描述&#xff08;来源&#xff09; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 思路 需创建一个头结点&#xff0c;然后从两个链表的表头开始依次比较传入的两个链表的结点的大小&#xff0c;并将两个链表中较小的…

Docker构建Golang项目常见问题

Docker构建Golang项目常见问题 1 dockerfile报错&#xff1a;failed to read expected number of bytes: unexpected EOF2 go mod tidy: go.mod file indicates go 1.21, but maximum supported version is 1.17 1 dockerfile报错&#xff1a;failed to read expected number o…

Linux编辑器gcc/g++的使用以及Makefile的用法

gcc如何完成 格式 gcc [选项] 要编译的文件 [选项] [目标文件] gcc对code.c编译形成可执行文件mybin&#xff0c;十分推荐直接这样写&#xff0c;下面会有拆分写法&#xff08;不推荐&#xff09; gcc与我们使用过的编辑器无二&#xff0c;都需要经过 1. 预处理&#xff08;…

QAnything部署Mac m1环境

本次安装时Qanything已经更新到了v1.3.3&#xff0c;支持纯python安装。安装过程比较简单&#xff0c;如下&#xff1a; QAnything/README_zh.md at qanything-python-v1.3.1 netease-youdao/QAnything GitHub 首先需要用Anaconda3创建隔离环境&#xff0c;简要说明下Anaco…

Word分节后,页码不连续、转PDF每节后多出空白页解决办法

1. 问题图例 废话少说&#xff0c;先上图&#xff1a; 2. 问题分析 问题分析&#xff1a;出现以上问题的原因可能有&#xff0c; 未链接到上一节页面布局中节的起始位置设置为[奇数页] 3. 解决问题 若为【1. 未链接到上一节】导致该问题出现&#xff0c;则我们需要选中页脚…

PACNet CellNet(代码开源)|bulk数据作细胞分类,评估细胞命运性能的一大利器

文章目录 1.前言2.CellNet2.1CellNet简介2.2CellNet结果 3.PACNet3.1安装R包与加载R包3.2加载数据3.3开始训练和分类3.4可视化分类过程3.5可视化分类结果 4.细胞命运分类和免疫浸润比较 1.前言 今天冲浪看到一个细胞分类性能评估的R包——PACNet&#xff0c;它与转录组分析方法…

Easy GIS .NET GMap.Net

Easy GIS .NET & GMap.Net .NET 环境下非常简单的GIS地图开发库。 Easy GIS .NET 一个简单的GIS 桌面应用程序&#xff0c;实现了地图瓦片加载、shapefile文件和csv文件加载渲染、地图坐标系统设置及转换等等基本功能&#xff0c;非常简单易用。 Easy GIS .NET is an o…

论文浅尝 | QA-GNN:结合语言模型与知识图谱进行问答推理

笔记整理&#xff1a;项卓怡&#xff0c;浙江大学硕士&#xff0c;研究方向为生化大模型。 链接&#xff1a;https://arxiv.org/abs/2104.06378 Code&#xff1a;https://github.com/michiyasunaga/qagnn Citation: Yasunaga M, Ren H, Bosselut A, et al. QA-GNN: Reasoning w…

pytest学习-pytorch单元测试

pytorch单元测试 一.公共模块[common.py]二.普通算子测试[test_clone.py]三.集合通信测试[test_ccl.py]四.测试命令五.测试报告 希望测试pytorch各种算子、block、网络等在不同硬件平台,不同软件版本下的计算误差、耗时、内存占用等指标. 本文基于torch.testing._internal 一…