当前位置: 首页 > news >正文

Java设计模式:一、六大设计原则-06:依赖倒置原则

文章目录

  • 一、定义:依赖倒置原则
  • 二、模拟场景:依赖倒置原则
  • 三、违背方案:依赖倒置原则
    • 3.1 工程结构
    • 3.2 抽奖系统
      • **3.2.1 定义抽奖用户类**
      • 3.2.2 抽奖控制
    • 3.3 单元测试
  • 四、改善代码:依赖倒置原则
    • 4.1 工程结构
    • 4.2 抽奖控制改善
      • 4.2.1 定义抽奖用户类
      • 4.2.2 抽奖接口
      • 4.2.3 随机抽奖实现
      • 4.2.4 权重抽奖实现
      • 4.2.5 创建抽奖服务
    • 4.3 单元测试

一、定义:依赖倒置原则

  • 依赖倒置原则Dependence Inversion Principle,DIP
    • 在设计代码架构时,高层模块不应该依赖于底层模块,二者都应该依赖于抽象抽象不应该依赖于细节,细节应该依赖于抽象
    • 依赖倒置原则是实现开闭原则的重要途经之一,它降低了类之间的耦合,提高了系统的稳定性和可维护性,同时这样的代码一般更易读,且便于传承。

二、模拟场景:依赖倒置原则

  • 在互联网的营销活动中,经常为了拉新和促活,会做一些抽奖活动。这些抽奖活动的规则会随着业务的不断发展而调整。
    • 如随机抽奖、权重抽奖等。其中权重是指用户在当前系统中的一个综合排名,比如活跃度、贡献度等。
  • 模拟出抽奖的一个系统服务。
    • 如果是初次搭建这样的系统会怎么实现呢?
    • 这个系统是否有良好的扩展性和可维护性,同时在变动和新增业务时测试的复杂度是否高?

三、违背方案:依赖倒置原则

3.1 工程结构

design-1.6-0
|——src|——main|--java|--com.lino.design|--BetUser.java|--DrawControl.java|——test|--java|--com.lino.design.test|--ApiTest.java

3.2 抽奖系统

3.2.1 定义抽奖用户类

BetUser.java

package com.lino.design;/*** @description: 投注用户*/
public class BetUser {/*** 用户姓名*/private String userName;/*** 用户权重*/private int userWeight;public BetUser() {}public BetUser(String userName, int userWeight) {this.userName = userName;this.userWeight = userWeight;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public int getUserWeight() {return userWeight;}public void setUserWeight(int userWeight) {this.userWeight = userWeight;}
}
  • 这个类就是一个普通的对象类,其中包括了用户姓名和对应的权重,方便满足不同的抽奖方式。

3.2.2 抽奖控制

DrawControl.java

package com.lino.design;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;/*** @description: 抽奖控制*/
public class DrawControl {/*** 随机抽取指定数量的用户,作为中奖用户** @param list  用户列表* @param count 中奖用户数量* @return 中奖用户列表*/public List<BetUser> doDrawRandom(List<BetUser> list, int count) {// 集合数量很小直接返回if (list.size() <= count) {return list;}// 乱序集合Collections.shuffle(list);// 取出指定数量的中奖用户List<BetUser> prizeList = new ArrayList<>(count);for (int i = 0; i < count; i++) {prizeList.add(list.get(i));}return prizeList;}public List<BetUser> doDrawWeight(List<BetUser> list, int count) {// 按照权重排序list.sort(((o1, o2) -> {int e = o2.getUserWeight() - o1.getUserWeight();if (0 == e) {return 0;}return e > 0 ? 1 : -1;}));// 取出指定数量的中奖用户List<BetUser> prizeList = new ArrayList<>(count);for (int i = 0; i < count; i++) {prizeList.add(list.get(i));}return prizeList;}
}
  • 在一个类中实现两种不同的抽奖逻辑。随机抽奖和权重抽奖。

3.3 单元测试

ApiTest.java

@Test
public void test_DrawControl() {List<BetUser> betUserList = new ArrayList<>();betUserList.add(new BetUser("花花", 65));betUserList.add(new BetUser("豆豆", 43));betUserList.add(new BetUser("小白", 72));betUserList.add(new BetUser("笨笨", 89));betUserList.add(new BetUser("丑蛋", 10));DrawControl drawControl = new DrawControl();List<BetUser> prizeRandomUserList = drawControl.doDrawRandom(betUserList, 3);logger.info("随机抽奖,中奖用户名单:{}", JSON.toJSON(prizeRandomUserList));List<BetUser> prizeWeightUserList = drawControl.doDrawWeight(betUserList, 3);logger.info("权重抽奖,中奖用户名单:{}", JSON.toJSON(prizeWeightUserList));
}
  • 在初始化数据后分别调用两个接口方法进行测试。

测试结果

14:50:54.496 [main] INFO  com.lino.design.test.ApiTest - 随机抽奖,中奖用户名单:[{"userWeight":89,"userName":"笨笨"},{"userWeight":10,"userName":"丑蛋"},{"userWeight":65,"userName":"花花"}]
14:50:54.527 [main] INFO  com.lino.design.test.ApiTest - 权重抽奖,中奖用户名单:[{"userWeight":89,"userName":"笨笨"},{"userWeight":72,"userName":"小白"},{"userWeight":65,"userName":"花花"}]
  • 从测试结果上看,程序没有问题,验证结果正常。但是这样实现有什么问题呢?
  • 如果程序是一次性的、几乎不变的,那么不考虑很多的扩展性和可维护性因素。
    • 但如果这些程序具有不确定性,或者当业务发展时需要不断地调整和新增,那么这样的实现方式就很不友好了。
  • 首先,这样的实现方式扩展起来很麻烦,每次扩展都需要新增接口,同时对于调用方来说需要新增调用接口的代码。
    • 其次,对于这个服务类来说,随着接口数量的增加,代码行数会不断地暴增,最后难以维护。

四、改善代码:依赖倒置原则

4.1 工程结构

design-1.6-1
|——src|——main|--java|--com.lino.design|--BetUser.java|--DrawControl.java|--DrawRandom.java|--DrawWeightRank.java|--IDraw.java|——test|--java|--com.lino.design.test|--ApiTest.java

4.2 抽奖控制改善

4.2.1 定义抽奖用户类

BetUser.java

package com.lino.design;/*** @description: 投注用户*/
public class BetUser {/*** 用户姓名*/private String userName;/*** 用户权重*/private int userWeight;public BetUser() {}public BetUser(String userName, int userWeight) {this.userName = userName;this.userWeight = userWeight;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public int getUserWeight() {return userWeight;}public void setUserWeight(int userWeight) {this.userWeight = userWeight;}
}
  • 这个类就是一个普通的对象类,其中包括了用户姓名和对应的权重,方便满足不同的抽奖方式。

4.2.2 抽奖接口

IDraw.java

package com.lino.design;import java.util.List;/*** @description: 抽奖接口*/
public interface IDraw {/*** 抽奖** @param list  用户列表* @param count 中奖数量* @return 中奖用户列表*/List<BetUser> prize(List<BetUser> list, int count);
}
  • 定义一个抽奖接口,接口中包括了需要传输的 list 集合,以及中奖用户数量。

4.2.3 随机抽奖实现

DrawRandom.java

package com.lino.design;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;/*** @description: 随机抽奖*/
public class DrawRandom implements IDraw {@Overridepublic List<BetUser> prize(List<BetUser> list, int count) {// 集合数量很小直接返回if (list.size() <= count) {return list;}// 乱序集合Collections.shuffle(list);// 取出指定数量的中奖用户List<BetUser> prizeList = new ArrayList<>(count);for (int i = 0; i < count; i++) {prizeList.add(list.get(i));}return prizeList;}
}

4.2.4 权重抽奖实现

DrawWeightRank.java

package com.lino.design;import java.util.ArrayList;
import java.util.List;/*** @description: 权重抽奖*/
public class DrawWeightRank implements IDraw {@Overridepublic List<BetUser> prize(List<BetUser> list, int count) {// 按照权重排序list.sort(((o1, o2) -> {int e = o2.getUserWeight() - o1.getUserWeight();if (0 == e) {return 0;}return e > 0 ? 1 : -1;}));// 取出指定数量的中奖用户List<BetUser> prizeList = new ArrayList<>(count);for (int i = 0; i < count; i++) {prizeList.add(list.get(i));}return prizeList;}
}

4.2.5 创建抽奖服务

DrawControl.java

package com.lino.design;import java.util.List;/*** @description: 抽奖控制*/
public class DrawControl {/*** 抽奖接口*/private IDraw draw;/*** 发起抽奖** @param draw        抽奖类型* @param betUserList 用户列表* @param count       中奖数量* @return 中奖的用户列表*/public List<BetUser> doDraw(IDraw draw, List<BetUser> betUserList, int count) {return draw.prize(betUserList, count);}
}
  • 这个类中提现了依赖倒置的重要性,可以把任何一种抽奖逻辑传递给这个类。
  • 这样实现的好处是可以不断地扩展,但是不需要在外部新增调用接口,降低了一套代码的维护成本,并提高了可扩展性及可维护性。

4.3 单元测试

ApiTest.java

@Test
public void test_DrawControl() {List<BetUser> betUserList = new ArrayList<>();betUserList.add(new BetUser("花花", 65));betUserList.add(new BetUser("豆豆", 43));betUserList.add(new BetUser("小白", 72));betUserList.add(new BetUser("笨笨", 89));betUserList.add(new BetUser("丑蛋", 10));DrawControl drawControl = new DrawControl();List<BetUser> prizeRandomUserList = drawControl.doDraw(new DrawRandom(), betUserList, 3);logger.info("随机抽奖,中奖用户名单:{}", JSON.toJSON(prizeRandomUserList));List<BetUser> prizeWeightUserList = drawControl.doDraw(new DrawWeightRank(), betUserList, 3);logger.info("权重抽奖,中奖用户名单:{}", JSON.toJSON(prizeWeightUserList));
}

测试结果

15:06:03.242 [main] INFO  com.lino.design.test.ApiTest - 随机抽奖,中奖用户名单:[{"userWeight":72,"userName":"小白"},{"userWeight":43,"userName":"豆豆"},{"userWeight":10,"userName":"丑蛋"}]
15:06:03.273 [main] INFO  com.lino.design.test.ApiTest - 权重抽奖,中奖用户名单:[{"userWeight":89,"userName":"笨笨"},{"userWeight":72,"userName":"小白"},{"userWeight":65,"userName":"花花"}]
  • 这里与前面代码唯一不同的是新增了实现抽奖的入参 new DrawRandom()new DrawWeightRank()
    • 在这两个抽奖的功能逻辑作为入参后,扩展起来会非常的方便。
  • 以这种抽象接口为基准搭建起来的框架结构会更加稳定,算程已经建设好,外部只需要实现自己的算子即可,最终把算子交给算程处理。

相关文章:

Java设计模式:一、六大设计原则-06:依赖倒置原则

文章目录 一、定义&#xff1a;依赖倒置原则二、模拟场景&#xff1a;依赖倒置原则三、违背方案&#xff1a;依赖倒置原则3.1 工程结构3.2 抽奖系统**3.2.1 定义抽奖用户类**3.2.2 抽奖控制 3.3 单元测试 四、改善代码&#xff1a;依赖倒置原则4.1 工程结构4.2 抽奖控制改善4.2…...

信息系统数据同步解决方案

实施数据同步解决方案时&#xff0c;重要的是确保数据同步是安全的、可靠的&#xff0c;并且能够适应系统变化。定期测试和监控数据同步过程&#xff0c;以确保其稳定运行&#xff0c;并随着需求的变化进行适当的调整和优化。 应用场景&#xff1a;信息系统A和信息系统B实现员…...

LRU算法 vs Redis近似LRU算法

LRU(Least Recently Use)算法&#xff0c;是用来判断一批数据中&#xff0c;最近最少使用算法。它底层数据结构由Hash和链表结合实现&#xff0c;使用Hash是为了保障查询效率为O(1)&#xff0c;使用链表保障删除元素效率为O(1)。 LRU算法是用来判断最近最少使用到元素&#xf…...

浅析ARMv8体系结构:异常处理机制

文章目录 概述异常类型中断终止Abort复位Reset系统调用 异常处理流程异常入口异常返回异常返回地址 堆栈选择 异常向量表异常向量表的配置 同步异常解析相关参考 概述 异常处理指的是处理器在运行过程中发生了外部事件&#xff0c;导致处理器需要中断当前执行流程转而去处理异…...

Golang开发--Goroutine的使用

Go 语言天生支持并发编程&#xff0c;提供了丰富的原语和工具来编写并发程序。Goroutine 是 Go 语言中的轻量级执行单位。它们是由 Go 运行时&#xff08;runtime&#xff09;管理的&#xff0c;并且能够在单个线程上运行成千上万个 Goroutine。创建 Goroutine 非常高效&#x…...

【Linux】package ‘python-yaml‘ has no installation candidate 如何解决

要解决此问题&#xff0c;可以尝试以下几个步骤&#xff1a; 确保系统已经更新到最新版本。可以使用以下命令进行系统更新&#xff1a; sudo apt update sudo apt upgrade确保您的软件源列表中包含了正确的软件源。可以使用以下命令编辑软件源列表&#xff1a; sudo nano /etc/…...

Selector选择器在AspNetCore中的用法

Selector选择器在AspNetCore中的用法 背景 项目编辑过程中会选择其所属的上级项目&#xff0c;而上级项目在数据结构中是以ParentID的方式表达&#xff0c;而非Project类型&#xff0c;用户不会记录也不应该记录ID值&#xff0c;因此应提供Selector项目下拉框供用户选择。 但…...

anaconda3最新版安装|使用详情|Error: Please select a valid Python interpreter

Win11查看安装的Python路径及安装的库 anaconda3最新版安装|使用详情|Error: Please select a valid Python interpreter 介绍开源包管理系统和环境管理系统 &#xff0c;包括多种语言的包安装&#xff0c;运行&#xff0c;更新&#xff0c;删除&#xff0c;最重要的是可以解…...

java八股文面试[多线程]——锁的分类

1.1 可重入锁、不可重入锁 Java中提供的synchronized&#xff0c;ReentrantLock&#xff0c;ReentrantReadWriteLock都是可重入锁。 重入&#xff1a;当前线程获取到A锁&#xff0c;在获取之后尝试再次获取A锁是可以直接拿到的。 不可重入&#xff1a;当前线程获取到A锁&…...

儿童安全门和围栏,以及游戏围栏等美国站要求的合规标准是什么?

儿童安全门和围栏 儿童安全门和围栏用于在门口&#xff08;如门道&#xff09;内设置围栏&#xff0c;或用作自支撑围栏&#xff0c;将幼儿可能在其中活动的区域围起来。这些商品可能由塑料、金属、乙烯树脂或木制组件等材料制成。此政策包括但不限于可扩展围栏、伸缩安全门和…...

kafka配合ElasticStack技术栈的搭配使用

今日内容: - kafka生产环境调优; - kafka配合ElasticStack技术栈的搭配使用; - zookeeper集群部署; - zookeeper的ACL; - zookeeper的调优; - PB级别项目; - ES8集群搭建/elk; (待定...) 订阅1个的topic: 老男孩: 10 多个不同的主题…...

对极几何与三角化求3D空间坐标

一&#xff0c;使用对极几何约束求R,T 第一步&#xff1a;特征匹配。提取出有效的匹配点 void find_feature_matches(const Mat &img_1, const Mat &img_2,std::vector<KeyPoint> &keypoints_1,std::vector<KeyPoint> &keypoints_2,std::vector&l…...

英语语法笔记

1.英语五大句型 主谓&#xff08;主语动词&#xff09; 主谓宾&#xff08;主语动词宾语&#xff09; 主谓宾宾&#xff08;主语动词简接宾语直接宾语&#xff09; 主谓宾补&#xff08;主语动词宾语宾语补语&#xff09; 主系表&#xff08;主语系动词主语补语&#xff09; 1…...

ES6的面向对象编程以及ES6中的类和对象

一、面向对象 1、面向对象 &#xff08;1&#xff09;是一种开发思想&#xff0c;并不是具体的一种技术 &#xff08;2&#xff09;一切事物均为对象&#xff0c;在项目中主要是对象的分工协作 2、对象的特征 &#xff08;1&#xff09;对象是属性和行为的结合体 &#x…...

ConfigMaps in K8s

摘要 ConfigMaps是Kubernetes&#xff08;K8s&#xff09;中用于存储应用程序配置信息的一种资源对象。它将key-value对存储为Kubernetes集群中的一个资源&#xff0c;并可以在Pod中以卷或环境变量的形式使用。 ConfigMaps的设计目的是将应用程序配置与应用程序本身解耦。它可…...

《机器人学一(Robotics(1))》_台大林沛群 第 6 周 【轨迹规划_直线转折处抛物线平滑】Quiz 6

步骤&#xff1a; 1、 编程 将PPT 的例子 跑一遍&#xff0c; 确保代码无误 2、根据题目 修改 相关参数 文章目录 求解代码_Python 解决的问题&#xff1a; 线段间转折点 的 速度 不连续 解决方法&#xff1a; 将直线段 两端 修正为 二次方程式 二次项圆滑 求解代码_Python …...

关于vscode的GitLens插件里的FILE HISTORY理解

最近在用vscode的GitLens插件开发项目遇到这个疑问&#xff0c;先看图&#xff1a; 每当我点击FILE HISTORY 一个commit时&#xff0c;正常来说显示器会自动将点击的提交版本和它上一个提交版本进行比较&#xff0c;如果单纯这么理解的话就错了&#xff0c;因为GitLens的File …...

通过idea实现springboot集成mybatys

概述 使用springboot 集成 mybatys后&#xff0c;通过http请求接口&#xff0c;使得通过http请求可以直接直接操作数据库&#xff1b; 完成后端功能框架&#xff1b;前端是准备上小程序&#xff0c;调用https的请求接口用。简单实现后端框架&#xff1b; 详细 springboot 集…...

力扣(LeetCode)算法_C++——移位字符串分组

给定一个字符串&#xff0c;对该字符串可以进行 “移位” 的操作&#xff0c;也就是将字符串中每个字母都变为其在字母表中后续的字母&#xff0c;比如&#xff1a;“abc” -> “bcd”。这样&#xff0c;我们可以持续进行 “移位” 操作&#xff0c;从而生成如下移位序列&am…...

Vue2 与Vue3的区别?面试题

Vue 2和Vue 3是Vue.js框架的不同版本&#xff0c;在面试中经常涉及到它们之间的区别。以下是Vue 2和Vue 3的主要区别&#xff1a; 性能提升&#xff1a;Vue 3在性能方面进行了优化。Vue 3引入了更高效的Diff算法&#xff0c;提高了渲染性能。此外&#xff0c;Vue 3还进行了代码…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...

DiscuzX3.5发帖json api

参考文章&#xff1a;PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下&#xff0c;适配我自己的需求 有一个站点存在多个采集站&#xff0c;我想通过主站拿标题&#xff0c;采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践

在 Kubernetes 集群中&#xff0c;如何在保障应用高可用的同时有效地管理资源&#xff0c;一直是运维人员和开发者关注的重点。随着微服务架构的普及&#xff0c;集群内各个服务的负载波动日趋明显&#xff0c;传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...

leetcode_69.x的平方根

题目如下 &#xff1a; 看到题 &#xff0c;我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历&#xff0c;我们是整数的平方根&#xff0c;所以我们分两…...