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:依赖倒置原则
文章目录 一、定义:依赖倒置原则二、模拟场景:依赖倒置原则三、违背方案:依赖倒置原则3.1 工程结构3.2 抽奖系统**3.2.1 定义抽奖用户类**3.2.2 抽奖控制 3.3 单元测试 四、改善代码:依赖倒置原则4.1 工程结构4.2 抽奖控制改善4.2…...
信息系统数据同步解决方案
实施数据同步解决方案时,重要的是确保数据同步是安全的、可靠的,并且能够适应系统变化。定期测试和监控数据同步过程,以确保其稳定运行,并随着需求的变化进行适当的调整和优化。 应用场景:信息系统A和信息系统B实现员…...

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

浅析ARMv8体系结构:异常处理机制
文章目录 概述异常类型中断终止Abort复位Reset系统调用 异常处理流程异常入口异常返回异常返回地址 堆栈选择 异常向量表异常向量表的配置 同步异常解析相关参考 概述 异常处理指的是处理器在运行过程中发生了外部事件,导致处理器需要中断当前执行流程转而去处理异…...
Golang开发--Goroutine的使用
Go 语言天生支持并发编程,提供了丰富的原语和工具来编写并发程序。Goroutine 是 Go 语言中的轻量级执行单位。它们是由 Go 运行时(runtime)管理的,并且能够在单个线程上运行成千上万个 Goroutine。创建 Goroutine 非常高效&#x…...
【Linux】package ‘python-yaml‘ has no installation candidate 如何解决
要解决此问题,可以尝试以下几个步骤: 确保系统已经更新到最新版本。可以使用以下命令进行系统更新: sudo apt update sudo apt upgrade确保您的软件源列表中包含了正确的软件源。可以使用以下命令编辑软件源列表: sudo nano /etc/…...
Selector选择器在AspNetCore中的用法
Selector选择器在AspNetCore中的用法 背景 项目编辑过程中会选择其所属的上级项目,而上级项目在数据结构中是以ParentID的方式表达,而非Project类型,用户不会记录也不应该记录ID值,因此应提供Selector项目下拉框供用户选择。 但…...

anaconda3最新版安装|使用详情|Error: Please select a valid Python interpreter
Win11查看安装的Python路径及安装的库 anaconda3最新版安装|使用详情|Error: Please select a valid Python interpreter 介绍开源包管理系统和环境管理系统 ,包括多种语言的包安装,运行,更新,删除,最重要的是可以解…...
java八股文面试[多线程]——锁的分类
1.1 可重入锁、不可重入锁 Java中提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是可重入锁。 重入:当前线程获取到A锁,在获取之后尝试再次获取A锁是可以直接拿到的。 不可重入:当前线程获取到A锁&…...

儿童安全门和围栏,以及游戏围栏等美国站要求的合规标准是什么?
儿童安全门和围栏 儿童安全门和围栏用于在门口(如门道)内设置围栏,或用作自支撑围栏,将幼儿可能在其中活动的区域围起来。这些商品可能由塑料、金属、乙烯树脂或木制组件等材料制成。此政策包括但不限于可扩展围栏、伸缩安全门和…...
kafka配合ElasticStack技术栈的搭配使用
今日内容: - kafka生产环境调优; - kafka配合ElasticStack技术栈的搭配使用; - zookeeper集群部署; - zookeeper的ACL; - zookeeper的调优; - PB级别项目; - ES8集群搭建/elk; (待定...) 订阅1个的topic: 老男孩: 10 多个不同的主题…...

对极几何与三角化求3D空间坐标
一,使用对极几何约束求R,T 第一步:特征匹配。提取出有效的匹配点 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.英语五大句型 主谓(主语动词) 主谓宾(主语动词宾语) 主谓宾宾(主语动词简接宾语直接宾语) 主谓宾补(主语动词宾语宾语补语) 主系表(主语系动词主语补语) 1…...

ES6的面向对象编程以及ES6中的类和对象
一、面向对象 1、面向对象 (1)是一种开发思想,并不是具体的一种技术 (2)一切事物均为对象,在项目中主要是对象的分工协作 2、对象的特征 (1)对象是属性和行为的结合体 &#x…...
ConfigMaps in K8s
摘要 ConfigMaps是Kubernetes(K8s)中用于存储应用程序配置信息的一种资源对象。它将key-value对存储为Kubernetes集群中的一个资源,并可以在Pod中以卷或环境变量的形式使用。 ConfigMaps的设计目的是将应用程序配置与应用程序本身解耦。它可…...

《机器人学一(Robotics(1))》_台大林沛群 第 6 周 【轨迹规划_直线转折处抛物线平滑】Quiz 6
步骤: 1、 编程 将PPT 的例子 跑一遍, 确保代码无误 2、根据题目 修改 相关参数 文章目录 求解代码_Python 解决的问题: 线段间转折点 的 速度 不连续 解决方法: 将直线段 两端 修正为 二次方程式 二次项圆滑 求解代码_Python …...

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

通过idea实现springboot集成mybatys
概述 使用springboot 集成 mybatys后,通过http请求接口,使得通过http请求可以直接直接操作数据库; 完成后端功能框架;前端是准备上小程序,调用https的请求接口用。简单实现后端框架; 详细 springboot 集…...
力扣(LeetCode)算法_C++——移位字符串分组
给定一个字符串,对该字符串可以进行 “移位” 的操作,也就是将字符串中每个字母都变为其在字母表中后续的字母,比如:“abc” -> “bcd”。这样,我们可以持续进行 “移位” 操作,从而生成如下移位序列&am…...
Vue2 与Vue3的区别?面试题
Vue 2和Vue 3是Vue.js框架的不同版本,在面试中经常涉及到它们之间的区别。以下是Vue 2和Vue 3的主要区别: 性能提升:Vue 3在性能方面进行了优化。Vue 3引入了更高效的Diff算法,提高了渲染性能。此外,Vue 3还进行了代码…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...