Java 中装饰者模式与策略模式在埋点系统中的应用

news2025/3/29 10:26:38

前言

在软件开发中,装饰者模式和策略模式是两种常用的设计模式,它们在特定的业务场景下能够发挥巨大的作用。本文将通过一个实际的埋点系统案例,探讨如何在 Java 中运用装饰者模式和策略模式,以及如何结合工厂方法模式来优化代码结构。

业务场景分析

随着互联网的发展,用户行为分析变得越来越重要,而埋点技术是实现用户行为分析的关键手段之一。埋点系统需要记录用户在应用中的各种操作行为,如点击、浏览、提交等,以便后续进行数据分析和业务决策。

假设我们正在开发一个在线教育平台,需要实现以下埋点功能:

  1. 点击埋点:记录用户点击的位置。

  2. 课程埋点:记录用户点击的课程信息。

  3. 任务埋点:记录用户点击的任务信息。

这些埋点功能需要根据不同的业务场景进行动态组合,例如在课程页面的点击操作需要记录点击位置和课程信息,而在任务页面的点击操作需要记录点击位置和任务信息。

装饰者模式的应用

装饰者模式允许我们在不修改原有代码的基础上,动态地给对象添加职责。它由以下几部分组成:

  • Component:定义对象的接口。

  • Concrete Component:实现 Component 接口的具体对象。

  • Decorator:维护一个对 Component 对象的引用,并定义与 Component 接口相同的接口。

  • Concrete Decorator:实现 Decorator 接口,负责给 Component 对象添加特定的职责。

实现埋点功能

// Component 接口
public interface SaveMessage {
    void saveMessage(PointSaveBean pointSaveBean, PointSaveRequest pointSaveRequest);
}

// Concrete Component:基础点击埋点
public class CommonClickPoint implements SaveMessage {
    @Override
    public void saveMessage(PointSaveBean pointSaveBean, PointSaveRequest pointSaveRequest) {
        pointSaveBean.setClickLocation(pointSaveRequest.getClickLocation());
    }
}

// Decorator 抽象类
public abstract class AddPointMessageService implements SaveMessage {
    protected SaveMessage saveMessage;

    public AddPointMessageService(SaveMessage saveMessage) {
        this.saveMessage = saveMessage;
    }

    @Override
    public void saveMessage(PointSaveBean pointSaveBean, PointSaveRequest pointSaveRequest) {
        saveMessage.saveMessage(pointSaveBean, pointSaveRequest);
    }
}

// Concrete Decorator:课程埋点
public class CourseClickPoint extends AddPointMessageService {
    public CourseClickPoint(SaveMessage saveMessage) {
        super(saveMessage);
    }

    @Override
    public void saveMessage(PointSaveBean pointSaveBean, PointSaveRequest pointSaveRequest) {
        super.saveMessage(pointSaveBean, pointSaveRequest);
        pointSaveBean.setCourseId(pointSaveRequest.getCourseId());
    }
}

// Concrete Decorator:任务埋点
public class TaskClickPoint extends AddPointMessageService {
    public TaskClickPoint(SaveMessage saveMessage) {
        super(saveMessage);
    }

    @Override
    public void saveMessage(PointSaveBean pointSaveBean, PointSaveRequest pointSaveRequest) {
        super.saveMessage(pointSaveBean, pointSaveRequest);
        pointSaveBean.setTaskId(pointSaveRequest.getTaskId());
    }
}

客户端代码

public class CommonMain {
    public static void main(String[] args) {
        // 初始化埋点类型列表
        List<PointSaveType> types = Arrays.asList(PointSaveType.TASK, PointSaveType.COURSE);

        // 初始化埋点保存对象
        PointSaveBean pointSaveBean = new PointSaveBean();

        // 初始化埋点请求对象
        PointSaveRequest pointSaveRequest = PointSaveRequest.builder()
                .pointSaveTypeList(types)
                .clickLocation("右上角落")
                .courseId("英语")
                .taskId("任务1")
                .build();

        // 初始化基础保存逻辑
        SaveMessage saveMessage = new CommonClickPoint();
        saveMessage.saveMessage(pointSaveBean, pointSaveRequest);
        System.out.println("基础埋点保存数据: " + pointSaveBean);

        // 根据埋点类型动态添加保存逻辑
        for (PointSaveType type : types) {
            if (type == PointSaveType.COURSE) {
                saveMessage = new CourseClickPoint(saveMessage);
            } else if (type == PointSaveType.TASK) {
                saveMessage = new TaskClickPoint(saveMessage);
            }
            saveMessage.saveMessage(pointSaveBean, pointSaveRequest);
        }

        // 打印最终埋点数据
        System.out.println("最终埋点保存数据: " + pointSaveBean);
    }
}

策略模式的应用

策略模式定义了一系列算法,并将每个算法封装到具有共同接口的独立类中,使它们可以互相替换。当埋点逻辑之间存在复杂的组合关系时,结合策略模式可以更好地管理这些组合逻辑。

实现埋点功能

// 策略接口
public interface PointSaveStrategy {
    void save(PointSaveBean pointSaveBean, PointSaveRequest pointSaveRequest);
}

// 具体策略:点击埋点
public class ClickPointSaveStrategy implements PointSaveStrategy {
    @Override
    public void save(PointSaveBean pointSaveBean, PointSaveRequest pointSaveRequest) {
        pointSaveBean.setClickLocation(pointSaveRequest.getClickLocation());
    }
}

// 具体策略:课程埋点
public class CoursePointSaveStrategy implements PointSaveStrategy {
    @Override
    public void save(PointSaveBean pointSaveBean, PointSaveRequest pointSaveRequest) {
        pointSaveBean.setCourseId(pointSaveRequest.getCourseId());
    }
}

// 具体策略:任务埋点
public class TaskPointSaveStrategy implements PointSaveStrategy {
    @Override
    public void save(PointSaveBean pointSaveBean, PointSaveRequest pointSaveRequest) {
        pointSaveBean.setTaskId(pointSaveRequest.getTaskId());
    }
}

// 策略上下文
public class PointSaveStrategyContext {
    private List<PointSaveStrategy> strategies = new ArrayList<>();

    public void addStrategy(PointSaveStrategy strategy) {
        strategies.add(strategy);
    }

    public void execute(PointSaveBean pointSaveBean, PointSaveRequest pointSaveRequest) {
        for (PointSaveStrategy strategy : strategies) {
            strategy.save(pointSaveBean, pointSaveRequest);
        }
    }
}

// 策略配置
public class PointSaveStrategyConfig {
    private static final Map<PointSaveType, PointSaveStrategy> STRATEGY_MAP = new HashMap<>();

    static {
        STRATEGY_MAP.put(PointSaveType.CLICK, new ClickPointSaveStrategy());
        STRATEGY_MAP.put(PointSaveType.COURSE, new CoursePointSaveStrategy());
        STRATEGY_MAP.put(PointSaveType.TASK, new TaskPointSaveStrategy());
    }

    public static PointSaveStrategy getStrategy(PointSaveType type) {
        return STRATEGY_MAP.getOrDefault(type, null);
    }
}

客户端代码

public class CommonMain {
    public static void main(String[] args) {
        // 初始化埋点类型列表
        List<PointSaveType> types = Arrays.asList(PointSaveType.TASK, PointSaveType.COURSE);

        // 初始化埋点保存对象
        PointSaveBean pointSaveBean = new PointSaveBean();

        // 初始化埋点请求对象
        PointSaveRequest pointSaveRequest = PointSaveRequest.builder()
                .pointSaveTypeList(types)
                .clickLocation("右上角落")
                .courseId("英语")
                .taskId("任务1")
                .build();

        // 创建策略上下文
        PointSaveStrategyContext context = new PointSaveStrategyContext();

        // 添加基础策略
        context.addStrategy(new ClickPointSaveStrategy());

        // 根据埋点类型动态添加策略
        for (PointSaveType type : types) {
            PointSaveStrategy strategy = PointSaveStrategyConfig.getStrategy(type);
            if (strategy != null) {
                context.addStrategy(strategy);
            }
        }

        // 执行所有策略
        context.execute(pointSaveBean, pointSaveRequest);

        // 打印最终埋点数据
        System.out.println("最终埋点保存数据: " + pointSaveBean);
    }
}

工厂方法模式的结合

为了进一步简化客户端代码,我们可以引入工厂方法模式来创建装饰者对象。

// 工厂类
public class PointSaveDecoratorFactory {
    public static SaveMessage getDecorator(PointSaveType type, SaveMessage saveMessage) {
        switch (type) {
            case COURSE:
                return new CourseClickPoint(saveMessage);
            case TASK:
                return new TaskClickPoint(saveMessage);
            default:
                System.out.println("未知的埋点类型: " + type);
                return saveMessage;
        }
    }
}

客户端代码优化

public class CommonMain {
    public static void main(String[] args) {
        // 初始化埋点类型列表
        List<PointSaveType> types = Arrays.asList(PointSaveType.TASK, PointSaveType.COURSE);

        // 初始化埋点保存对象
        PointSaveBean pointSaveBean = new PointSaveBean();

        // 初始化埋点请求对象
        PointSaveRequest pointSaveRequest = PointSaveRequest.builder()
                .pointSaveTypeList(types)
                .clickLocation("右上角落")
                .courseId("英语")
                .taskId("任务1")
                .build();

        // 初始化基础保存逻辑
        SaveMessage saveMessage = new CommonClickPoint();
        saveMessage.saveMessage(pointSaveBean, pointSaveRequest);
        System.out.println("基础埋点保存数据: " + pointSaveBean);

        // 根据埋点类型动态添加保存逻辑
        for (PointSaveType type : types) {
            saveMessage = PointSaveDecoratorFactory.getDecorator(type, saveMessage);
            saveMessage.saveMessage(pointSaveBean, pointSaveRequest);
        }

        // 打印最终埋点数据
        System.out.println("最终埋点保存数据: " + pointSaveBean);
    }
}

总结

在实际的开发过程中,合理地运用设计模式能够使我们的代码更加灵活、可维护和可扩展。装饰者模式适合用于在运行时动态地给对象添加职责,而策略模式则适合用于管理多种算法或行为的组合。通过结合工厂方法模式,我们可以进一步简化客户端代码,使系统更加模块化和易于使用。

通过本文的示例,我们看到了装饰者模式和策略模式在埋点系统中的有效应用。这些设计模式不仅解决了实际的业务问题,还为我们提供了应对复杂需求变化的优雅解决方案。在未来的开发中,我们可以根据具体的需求场景,灵活地选择和结合不同的设计模式,以构建高质量的软件系统。

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

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

相关文章

无人设备遥控器之调度自动化技术篇

一、技术原理 信息采集与处理&#xff1a; 通过传感器、仪表等设备采集无人设备的各种数据&#xff0c;如位置、速度、状态等。 将采集到的数据传输到调度自动化系统中进行处理和分析&#xff0c;以获取设备的实时状态。 系统建模与优化&#xff1a; 调度自动化系统会根据…

【AI】Orin Nano+ubuntu22.04上移植YoloV11,并使用DeepStream测试成功

【AI】郭老二博文之:AI学习目录汇总 1、准备工作 使用 sdk-manager 烧写 OrinNano, JetPack版本为6.0 DP,对应操作系统为:Ubuntu22.04 参见博客:【NVIDIA】Jetson Orin Nano系列:烧写Ubuntu22.04 2、安装 PyTorch 2.1 下载依赖 1)安装onnx pip install onnx -i h…

K8S学习之基础四十五:k8s中部署elasticsearch

k8s中部署elasticsearch 安装并启动nfs服务yum install nfs-utils -y systemctl start nfs systemctl enable nfs.service mkdir /data/v1 -p echo /data/v1 *(rw,no_root_squash) >> /etc/exports exports -arv systemctl restart nfs创建运行nfs-provisioner需要的sa账…

如何在 Windows 上安装并使用 Postman?

Postman 是一个功能强大的API测试工具&#xff0c;它可以帮助程序员更轻松地测试和调试 API。在本文中&#xff0c;我们将讨论如何在 Windows 上安装和使用 Postman。 Windows 如何安装和使用 Postman 教程&#xff1f;

Axure RP 9.0教程: 基于动态面板的元件跟随来实现【音量滑块】

文章目录 引言I 音量滑块的实现步骤添加底层边框添加覆盖层基于覆盖层创建动态面板添加滑块按钮设置滑块拖动效果引言 音量滑块在播放器类APP应用场景相对较广,例如调节视频的亮度、声音等等。 I 音量滑块的实现步骤 添加底层边框 在画布中添加一个矩形框:500 x 32,圆…

WPF 与 C# 开发深度剖析

一、引言 在当今的软件开发领域&#xff0c;Windows 平台依旧占据着重要的地位。而 WPF&#xff08;Windows Presentation Foundation&#xff09;作为微软推出的一款强大的用户界面&#xff08;UI&#xff09;框架&#xff0c;为开发者提供了丰富的功能和灵活的设计方式&…

好消息!软航文档控件(NTKO WebOffice)在Chrome 133版本上提示扩展已停用的解决方案

软航文档控件现有版本依赖Manifest V2扩展技术支持才能正常运行&#xff0c;然而这个扩展技术到2025年6月在Chrome高版本上就彻底不支持了&#xff0c;现在Chrome 133开始的版本已经开始弹出警告&#xff0c;必须手工开启扩展支持才能正常运行。那么如何解决这个技术难题呢&…

通过仿真确定抗积分饱和策略的最佳系数

通过仿真确定抗积分饱和策略的最佳系数&#xff08;如PID参数 ( K_p, K_i, K_d ) 以及抗饱和参数 ( K_{\text{back}} )、积分限幅值等&#xff09;是一个系统化的过程。以下是具体步骤和示例&#xff1a; — 1. 建立仿真模型 1.1 模型组成 被控对象&#xff1a;例如电机、温…

消息队列(Kafka及RocketMQ等对比联系)

目录 消息队列 一、为什么使用消息队列&#xff1f;消息队列有什么优点/缺点&#xff1f;介绍下Kafka、ActiveMQ、RabbitMQ、RocketMQ有什么优点缺点&#xff0c;如何取舍&#xff1f; 1.公司业务场景是什么&#xff0c;这个业务场景有什么挑战&#xff0c;如果不用MQ有什么麻…

GitHub开源的容器管理面板-Dpanel

dpanel Docker安装部署二进制部署 GitHub官网 一块轻量化docker可视化管理面板&#xff0c;由国人开发&#xff0c;个人觉得是比较好用的&#xff0c;功能都很齐全&#xff0c;并且可以通过修改源码&#xff0c;自定义前端样式等。 Docker安装部署 官网 部署环境&#xff1…

【HarmonyOS Next】三天撸一个BLE调试精灵

【HarmonyOS Next】三天撸一个BLE调试精灵 一、功能介绍 BLE调试精灵APP属于工具类APP&#xff0c;在用户使用的过程中&#xff0c;负责调试BLE设备从机端&#xff0c;比如蓝牙耳机、低功耗设备、带有BLE的空调等设备&#xff0c;可以在页面中清晰看到设备的厂商&#xff0c;…

java 批量下载doc\excle\pdf

指定图片集合 下载到指定文件夹 import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.Arrays; import java.util.List;public class OfficeFileDownloader {/*** 需要下载的Office文档URL列表*/private static final List<Strin…

软件性能效率测试工具有哪些?专业第三方软件检测机构推荐

在软件开发的新时代&#xff0c;软件性能效率测试已经成为每个企业不可或缺的一部分。无论是在竞争激烈的市场中&#xff0c;还是在追求卓越用户体验的过程中&#xff0c;都需要进行有效的性能测试。 一、软件性能效率测试的目标   1、响应时间&#xff1a;确保用户请求的响…

使用flask_restful快速构建接口

Flask-RESTful 是一个用于快速构建 RESTful API 的 Flask 扩展。它简化了创建、管理和文档化 REST API 的过程。利用 Flask-RESTful&#xff0c;你可以更容易地将你的 Flask 应用程序组织成 RESTful 原则的风格 安装包 pip install flask_restful 快速构建接口 from flask im…

centos 7 部署FTP 服务用shell 搭建脚本,使用时稍微修改自己所需需求

#!/bin/bash # 检查是否为 root 用户 if [ "$(id -u)" ! "0" ]; then echo "此脚本需要以 root 用户身份运行。" exit 1 fi # 安装 vsftpd yum install vsftpd -y # 备份原始配置文件 cp /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd…

Hadoop集群搭建(hdfs、yarn)

Hadoop 是 Apache 软件基金会旗下的一个开源项目&#xff0c;是用于处理大数据的分布式系统基础架构&#xff0c;被广泛应用于大数据存储、处理和分析等场景。 一、核心组件 1、Hadoop 分布式文件系统&#xff08;HDFS&#xff09; 具有高容错性&#xff0c;能在低成本硬件上…

Keepalived 实现高可用方案

Keepalived简介 ‌Keepalived‌ 是一个基于 ‌VRRP&#xff08;Virtual Router Redundancy Protocol&#xff09;协议‌的高可用性解决方案&#xff0c;主要用于实现‌服务故障自动切换&#xff08;Failover&#xff09;和负载均衡‌。通过管理虚拟 IP&#xff08;VIP&#xf…

医学图像分割数据集肺分割数据labelme格式6299张2类别

数据集格式&#xff1a;labelme格式(不包含mask文件&#xff0c;仅仅包含jpg图片和对应的json文件) 图像分辨率&#xff1a;1024x1024 图片数量(jpg文件个数)&#xff1a;6299 标注数量(json文件个数)&#xff1a;6299 标注类别数&#xff1a;2 标注类别名称:["leftl…

C语言复习笔记--函数递归

在学习了函数之后,函数递归是我们必然会接触到的课题,下面就让我们看下函数递归相关的知识. 递归是什么&#xff1f; 递归这个词看着就不那么好理解,那么什么是递归呢?递归其实是⼀种解决问题的⽅法,在C语⾔中,递归就是函数自己调用自己. 写⼀个史上最简单的C语⾔递归代码: …

husky的简介以及如果想要放飞自我的解决方案

husky 是一个 Git Hooks 管理工具&#xff0c;它的主要作用是 在 Git 提交&#xff08;commit&#xff09;、推送&#xff08;push&#xff09;等操作时执行自定义脚本&#xff0c;比如代码检查&#xff08;Lint&#xff09;、单元测试&#xff08;Test&#xff09;、格式化代码…