策略模式详解

news2024/11/18 21:51:50

文章目录

  • 策略模式(行为模式)
    • 1. 策略模式介绍
    • 2. 好处
    • 3. 场景案例
    • 4. 案例源码
      • 1. 代码结构
      • 2. 榜单服务接收消息入口
      • 3. 基础任务类
      • 4. 定义策略模式转发的规范
      • 5. 代理的第一层
      • 6. 代理的第二层抽象父类:定义视频聊榜单代理规范
      • 7. 代理的第二层实现子类
      • 8. 枚举
      • 9. XML 配置
      • 10. 策略模式榜单服务接收消息入口测试类

策略模式(行为模式)

1. 策略模式介绍

  • 将一系列同类型的算法放在一起,用一个算法封装起来,由独立于使用的客户端自己选择对应算法使用
  • 通常有以下角色:
    • 抽象策略(Strategy)类:它是公共接口,各种不同的算法以不同的方式实现这个接口,控制类使用这个接口调用不同的算法
    • 具体策略(Concrete Strategy)类:实现了抽象策略接口,提供具体的算法实现
    • 控制(Context)类:持有一个策略类的引用,统一提供实现算法的方法,最终给客户端调用

2. 好处

  • 替代 if/else 导致的代码混乱
  • 隔离不同算法在自己的类中,改动一个算法不会影响其他算法,符合单一职责原则

3. 场景案例

  • 分布式消息队列采用策略模式,处理大量类似的榜单维护
    • 当前项目分布式队列使用的是点对点通信,即动作发生(如:登录、注册、直播动作、坐等动作、视频聊动作等)时作为生产者发送消息、放入对应消息队列,队列处理程序作为消费者、循环顺序拉取消息队列中的单个消息进行处理
    • 我们大量榜单都是实时榜单,榜单更新时过滤条件多而改动频繁,且不同榜单过滤条件不一致
      • 视频聊热门榜单需要过滤开直播的用户,用户开直播下榜、关直播上榜
  • 当前解决方案
    • 用户发生动作时,生产消息到对应榜单队列处理程序,消费者根据过滤条件更新榜单
    • 由于条件很多且经常修改,导致每次都要修改很多个生产者,然后重启
  • 优化使用策略模式 + 静态代理(根据场景选择转发)+ 模板方法模式
    • 改变配置即可改变队列进行不同的推送、且不需要修改与重启任何生产者
    • 类似将分布式消息队列的点对点通信改成热加载的发布订阅模式
  • 设计思路
    • 加代理层,生产者动作触发后发送到代理层,代理层接收到消息再发送到对应消费者处理
    • 代理层使用 XML 配置的方式,配置场景(即生产者的动作)绑定处理业务(消费者处理程序)
    • 修改条件时,仅需要修改配置,然后重启代理层业务即可
    • 代理层注册时将榜单业务初始化到内存,然后监听生产者消息
    • 消息先发送到第一层总代理层,然后再根据配置发到第二层具体业务的代理层,通过此代理发消息到具体消费者程序处理
  • 整体类图
    在这里插入图片描述

4. 案例源码

1. 代码结构

├── IStrategyService.java
├── RankReceivingEntrance.java
├── enums
│   ├── JobSceneForwardTypes.java
│   └── VideoRankTypes.java
├── proxy
│   ├── AbstractVideoRankStrategy.java
│   ├── ModifyVideoRankHotStrategyImpl.java
│   ├── ModifyVideoRankRecommendStrategyImpl.java
│   └── StrategyContextService.java
└── register
    └── BaseJobService.java


└── resources
    └── strategy
        ├── JobMonitorForwardConfig.xml
        └── JobSceneForwardConfig.xml

2. 榜单服务接收消息入口

  • 根据场景注册对应服务,开启监听依次执行对应方法
public class RankReceivingEntrance extends BaseJobService {

    public boolean process(Map<String, String> map) {
        if (!validateParam(map)) {
            System.out.println("消息接收入口参数校验不正确 map:" + map);
            return false;
        }

        System.out.println("进入榜单服务接收消息入口 map:" + map);

        // 传入场景 详见枚举
        String scene = map.getOrDefault("scene", "0");

        try {
            // 注册策略服务 注册视频聊榜单具体子类列表
            doRegisterForward(JobSceneForwardTypes.VIDEO_RANK_CONSUMER_TYPES, scene);

            // 开启监听 执行具体策略操作
            doMonitorForward(map);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }

    /**
     * 参数校验
     * @return {@code true} 参数校验正确
     */
    private boolean validateParam(Map<String,String> map) {
        if (CollectionUtils.isEmpty(map)) {
            return false;
        }
        return true;
    }
}

3. 基础任务类

public abstract class BaseJobService{

	private List<IStrategyService> strategyServiceList;

	/**
	 * 个性化分发服务 不需要每个子类都实现
	 * 注册策略服务 注册视频聊榜单具体子类列表
	 * @param jobSceneForwardTypes 根据对应的 scene 转发到配置中
	 * @param scene 场景
	 */
	protected void doRegisterForward(JobSceneForwardTypes jobSceneForwardTypes, String scene) throws Exception {
		if(jobSceneForwardTypes == null){
			System.out.println("注册服务处理转发类 参数校验失败:" + jobSceneForwardTypes);
			return;
		}
		// 根据场景获取对应转发 type 节点
		List<Element> typeList = getForwardTypeList(jobSceneForwardTypes, scene);

		// 根据 type 节点获取对应 bean 节点
		List<Element> beanList = getForwardBeanList(typeList);

		// 根据对应 bean 节点实例化对象
		List<IStrategyService> strategyServiceList = newInstanceBeanList(beanList);

		// 内存存储实例化对象
		setStrategyServiceList(strategyServiceList);
	}

	/**
	 * 根据场景获取对应转发 type 节点,遍历的文档 JobSceneForwardConfig.xml
	 * @param jobSceneForwardTypes 根据对应的 scene 转发到配置中
	 * @param scene 场景
	 * @return 文档所有 type 节点
	 */
	private List<Element> getForwardTypeList(JobSceneForwardTypes jobSceneForwardTypes, String scene)
			throws Exception {
		List<Element> resultList = new ArrayList<>();

		String jsfcFilePath = System.getProperty("user.dir") + "/src/main/resources/strategy/JobSceneForwardConfig.xml";
		File file = new File(jsfcFilePath);
		if (null == file) {
			throw new RuntimeException("JobSceneForwardConfig.xml 文件找不到");
		}

		Document document = parse(file);
		List<Element> enumsList = document.selectNodes("/job/enums");

		// 遍历 JobSceneForwardConfig.xml 下 enums 根据 enumsName 获取对应的 list
		List<Element> typeList = new ArrayList<>();
		if (!CollectionUtils.isEmpty(enumsList)) {
			for (Element enumsElement : enumsList) {

				String enumsName = getAttribute(enumsElement, "name");
				System.out.println("根据场景获取对应转发type enumsName:" + enumsName
						+ " jobSceneForwardTypes:" + jobSceneForwardTypes);

				if (jobSceneForwardTypes.getType().equals(enumsName)) {
					typeList.addAll(enumsElement.selectNodes("list/type"));
					break;
				}
			}
		}

		// 遍历 JobSceneForwardConfig.xml 下对应的 enums/type 根据 scene-id 获取对应的 list
		if (!CollectionUtils.isEmpty(typeList)) {
			for(Element typeElement : typeList){

				List<Element> sceneList = typeElement.selectNodes("list/scene");
				// 遍历 enums/type 下的每个 scene 根据 scene 获取对应 type
				if(!CollectionUtils.isEmpty(sceneList)){
					for(Element sceneElement : sceneList){

						String sceneId = getAttribute(sceneElement, "id");
						System.out.println("根据场景获取对应转发type sceneId:" + sceneId + " scene:" + scene);

						// 如果id匹配 获取对应type
						if(scene != null && (scene).equals(sceneId)){
							resultList.add(typeElement);
							break;
						}
					}
				}
			}
		}

		System.out.println("根据场景获取对应转发type:" + resultList);
		return resultList;
	}

	/**
	 * 遍历的文档 JobSceneForwardConfig.xml
	 * @param ScenetypeList JobSceneForwardConfig.xml 所有 type 节点
	 * @return 根据 type 节点获取对应 bean 节点
	 */
	private List<Element> getForwardBeanList(List<Element> ScenetypeList) throws Exception {
		List<Element> resultList = new ArrayList<>();

		// 根据对应的转发配置、执行对应的方法
        String jmfcFilePath = System.getProperty("user.dir") + "/src/main/resources/strategy/JobMonitorForwardConfig.xml";
        File file = new File(jmfcFilePath);
		if (null == file) {
			throw new RuntimeException("JobMonitorForwardConfig.xml 文件找不到");
		}

		Document document = parse(file);
		List<Element> monitorTypeList = document.selectNodes("/job/type");

		// 遍历可使用的 typeList 以及 JobMonitorForwardConfig.xml 匹配对应 beanList
		if(!CollectionUtils.isEmpty(ScenetypeList) && !CollectionUtils.isEmpty(monitorTypeList)){
			for(Element sceneTypeElement : ScenetypeList){
				for(Element monitorTypeElement : monitorTypeList){

					String sceneTypeName = getAttribute(sceneTypeElement, "name");
					String monitorTypeName = getAttribute(monitorTypeElement, "name");
					System.out.println("根据type节点获取对应bean节点 sceneTypeName:" + sceneTypeName
							+ " monitorTypeName:" + monitorTypeName);

					if(sceneTypeName != null && sceneTypeName.equals(monitorTypeName)){
						resultList.addAll(monitorTypeElement.selectNodes("list/bean"));
						break;
					}
				}
			}
		}

		System.out.println("根据type节点获取对应bean节点:" + resultList);
		return resultList;
	}

	/**
	 * 根据对应 bean 节点实例化对象
	 * @param beanList 所有 bean 节点
	 * @return 所有实例化对象
	 */
	private <T> List<T> newInstanceBeanList(List<Element> beanList) {
		List<T> resultList = new ArrayList<>();
		if(!CollectionUtils.isEmpty(beanList)){
			for(Element beanElement : beanList){

				String className = getAttribute(beanElement, "class");
				if(!StringUtils.isEmpty(className)) {

					try {
						Class clazz = Class.forName(className);
						resultList.add((T) clazz.newInstance());
					} catch (ClassNotFoundException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InstantiationException e) {
						e.printStackTrace();
					}
				}
				System.out.println("根据对应bean节点实例化对象 className:" + className);
			}
		}
		System.out.println("根据对应bean节点实例化对象:" + resultList);
		return resultList;
	}


	/**
	 * 开启监听 执行具体策略操作
	 */
	protected void doMonitorForward(Map<String, String> map) {
		System.out.println("开启监听 执行具体策略操作 :" + strategyServiceList);
		if(!CollectionUtils.isEmpty(strategyServiceList)){

			for(IStrategyService strategyService : strategyServiceList){
				StrategyContextService strategyContextService = new StrategyContextService(strategyService);
				strategyContextService.doAction(map);

			}
		}
	}

    private static Document parse(File file) throws DocumentException {
        SAXReader reader = new SAXReader();
        Document document = reader.read(file);
        return document;
    }

    private static String getAttribute(Element ele, String attrName) {
        Attribute attribute = ele.attribute(attrName);
        return attribute == null ? null : attribute.getValue();
    }

	public List<IStrategyService> getStrategyServiceList() {
		return strategyServiceList;
	}

	public void setStrategyServiceList(List<IStrategyService> strategyServiceList) {
		this.strategyServiceList = strategyServiceList;
	}
}

4. 定义策略模式转发的规范

public interface IStrategyService {

    /**
     * 通知转发操作
     * 每一个模块都要重新此动作
     */
    void doNotifyOperation(Map<String, String> map);
}

5. 代理的第一层

  • 策略模式的总入口
public class StrategyContextService {

    private IStrategyService strategyService;

    public StrategyContextService(IStrategyService strategyService) {
        this.strategyService = strategyService;
    }

    /**
     * 所有的策略服务都必须先执行此操作
     * 代理层监听到消息,调用此方法
     */
    public void doAction(Map<String, String> map) {
        System.out.println("所有的策略服务都执行此操作 map:" + map);
        if (null == strategyService) {
            throw new RuntimeException("代理的第一层策略入口服务为空 map:" + map);
        }
        strategyService.doNotifyOperation(map);
    }

}

6. 代理的第二层抽象父类:定义视频聊榜单代理规范

public abstract class AbstractVideoRankStrategy implements IStrategyService {

    /**
     * 视频聊榜单都会执行此操作
     */
    @Override
    public void doNotifyOperation(Map<String, String> map) {
        System.out.println("视频聊榜单都会执行此操作 map:"+ map);
        if (!validateParam(map)) {
            System.out.println("代理的第二层参数校验不正确 map:" + map);
            return;
        }

        doNotifyReward(map);
    }

    /**
     * 参数校验
     * @return {@code true} 参数校验通过
     */
    protected abstract boolean validateParam(Map<String, String> map);

    /**
     * 延迟到子类进行转发,转发到具体消费者队列
     */
    protected abstract void doNotifyReward(Map<String, String> map);
}

7. 代理的第二层实现子类

  • 转发到视频聊热门榜单
  • 转发到视频聊推荐榜单
public class ModifyVideoRankHotStrategyImpl extends AbstractVideoRankStrategy {

    /**
     * 参数校验
     * @return {@code true} 参数校验通过
     */
    @Override
    protected boolean validateParam(Map<String, String> map) {
        if (CollectionUtils.isEmpty(map)) {
            return false;
        }
        return true;
    }

    /**
     * 转发到具体消费者队列
     */
    @Override
    protected void doNotifyReward(Map<String, String> map) {
        System.out.println("转发到视频聊热门榜单...");
    }
}
public class ModifyVideoRankRecommendStrategyImpl extends AbstractVideoRankStrategy {

    /**
     * 参数校验
     * @return {@code true} 参数校验通过
     */
    @Override
    protected boolean validateParam(Map<String, String> map) {
        if (CollectionUtils.isEmpty(map)) {
            return false;
        }
        return true;
    }

    /**
     * 转发到具体消费者队列
     */
    @Override
    protected void doNotifyReward(Map<String, String> map) {
        System.out.println("转发到视频聊推荐榜单...");
    }
}

8. 枚举

  • 根据对应的 scene 转发到配置中
  • 视频聊榜单生产者业务类型
@Getter
@ToString
public enum JobSceneForwardTypes {
    VIDEO_RANK_CONSUMER_TYPES("VideoRankConsumerTypes", "视频聊榜单处理转发"),
    ;

    private String type;
    private String desc;

    JobSceneForwardTypes(String type, String desc){
        this.type = type;
        this.desc = desc;
    }

}
@Getter
@ToString
public enum VideoRankTypes {
    USER_LOGIN("1", "用户登录"),
    USER_REGISTER("2", "注册"),
    ;

    private String type;
    private String desc;

    VideoRankTypes(String type, String desc) {
        this.type = type;
        this.desc = desc;
    }
}

9. XML 配置

  • JobSceneForwardConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<job>
    <!--注意需要重启此枚举对应的方法:strategy.enums.JobSceneForwardTypes-->
    <!-- 视频聊榜单转发处理 -->
    <enums name="VideoRankConsumerTypes">
        <list>
            <!--name 绑定 JobMonitorForwardConfig.xml 的 name 字段-->
            <type name="MODIFY_VIDEO_RANK_RECOMMEND" desc="更新视频聊推荐榜单">
                <!--视频聊榜单场景枚举:strategy.enums.VideoRankTypes-->
                <list>
                    <scene id="1" desc="USER_LOGIN-用户登录"/>
                    <scene id="2" desc="USER_REGISTER-用户注册"/>
                </list>
            </type>
            <type name="MODIFY_VIDEO_RANK_HOT" desc="更新视频聊热门榜单">
                <list>
                    <scene id="1" desc="USER_LOGIN-用户登录"/>
                </list>
            </type>
        </list>
    </enums>
</job>
  • JobMonitorForwardConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<job>
<!--更新视频聊推荐榜单-->
<!--name 绑定 JobSceneForwardConfig.xml 的 name 字段-->
<type name="MODIFY_VIDEO_RANK_RECOMMEND">
    <list>
        <bean class="strategy.proxy.ModifyVideoRankRecommendStrategyImpl" desc="更新视频聊推荐榜单" />
    </list>
</type>

<!--更新视频聊热门榜单-->
<type name="MODIFY_VIDEO_RANK_HOT">
    <list>
        <bean class="strategy.proxy.ModifyVideoRankHotStrategyImpl" desc="更新视频聊热门榜单" />
    </list>
</type>
</job>

10. 策略模式榜单服务接收消息入口测试类

public class RankReceivingEntranceTest {

    @Test
    public void test() {
        RankReceivingEntrance rankReceivingEntrance = new RankReceivingEntrance();

        Map<String, String> sceneMapLogin =  new HashMap<>();
        sceneMapLogin.put("scene", VideoRankTypes.USER_LOGIN.getType());
        rankReceivingEntrance.process(sceneMapLogin);

        System.out.println();

        Map<String, String> sceneMapRegister =  new HashMap<>();
        sceneMapRegister.put("scene", VideoRankTypes.USER_REGISTER.getType());
        rankReceivingEntrance.process(sceneMapRegister);
    }
}

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

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

相关文章

elasticsearch自定义企业词典

我们中文分词用的是ik&#xff0c;但是ik只是对基本的中文词进行了分词&#xff0c;而对于企业或者人名没有进行分词。比如&#xff0c;我搜索中国平安&#xff0c;那么ik只能分成中国、平安如果这样&#xff0c;这肯定是不行滴&#xff01;接下来&#xff0c;俺就教你&#xf…

历史上被发现的第一个真正的Bug - Grace Hopper

写在前面&#xff1a;博主是一只经过实战开发历练后投身培训事业的“小山猪”&#xff0c;昵称取自动画片《狮子王》中的“彭彭”&#xff0c;总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域&#xff0c;如今终有小成…

【bug】antd全局的主题色样式被覆盖,被修改为`antd`默认的主题色

背景&#xff1a; 项目本身修改了主题色,配置如下: // umi配置文件 export default {theme: {primary-color: #2F54EB, // 全局主色}, };需要对图片上传组件做封装,并在项目中统一引用,如下 import { TdsUpload } from tdsComponents;环境信息 node tiandstiandsdeMacBook…

【JavaEE】前后端分离实现博客系统(页面构建)

文章目录1 效果展示1.1 博客登录页面1.2 博客列表页面1.3 博客详情页面1.4 博客编辑页面2 页面具体实现2.1 博客列表页的实现2.2 博客详情页的实现2.3 博客登录页面的实现2.4 博客编辑页面的实现写在最后1 效果展示 1.1 博客登录页面 用于实现用户的登录功能&#xff0c;并展…

2023年3月西安/杭州/深圳/东莞NPDP产品经理认证考试报名

产品经理国际资格认证NPDP是国际公认的唯一的新产品开发专业认证&#xff0c;集理论、方法与实践为一体的全方位的知识体系&#xff0c;为公司组织层级进行规划、决策、执行提供良好的方法体系支撑。 【认证机构】 产品开发与管理协会&#xff08;PDMA&#xff09;成立于1979年…

什么是量子计算?

什么是量子计算&#xff1f; 量子计算机仍处于起步阶段&#xff0c;正在影响已经在经典计算机上运行的新一代模拟&#xff0c;现在使用 NVIDIA cuQuantum SDK 进行加速。 在史蒂夫乔布斯 (Steve Jobs) 推出可以放入口袋的计算机之前 27 年&#xff0c;物理学家保罗贝尼奥夫 (P…

[MySQL核心]2.select单表查询常见操作

MySQL核心--select单表查询常见操作select单表查询常见操作关于通配符*的使用结合MySQL运算符去重distinct空值查询union合并查询带in子查询(重点)limit分页查询排序order by分组group by笔试实践问题&#xff08;新浪&#xff09;select单表查询常见操作 关于通配符*的使用 项…

记录实现操作系统互斥锁的一次思考

今天实现操作系统互斥锁的时候遇到一个有趣的问题。 场景 有两个进程分别名为 taskA&#xff0c;taskB&#xff0c;采取时间片轮转的方式交替运行——也即维护了一个 ready_queue&#xff0c;根据时钟中断来 FIFO 地调度任务。它们的任务是无限循环调用 sys_print() 来打印自…

华为OD机试题,用 Java 解【用户调度问题】问题

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…

Java基础常识

目录 JDK和JRE和JVM分别是什么?有什么关系? 什么是字节码,采用字节码的好处是什么 ? Java 程序从源代码到运行的过程 为什么 Java语言"编译与解释并存" Java 和 C、Go 语言的区别&#xff0c;各自的优缺点&#xff1f; JDK和JRE和JVM分别是什么?有什么关系…

Flink相关介绍

简介 Flink的定位是&#xff1a;Apache Flink是一个框架和分布式处理引擎&#xff0c;如图所示&#xff0c;用于对无界和有界数据流进行有状态计算。Flink被设计在所有常见的集群环境运行&#xff0c;以内存执行速度和任意规模来执行计算。 Flink 框架处理流程应用场景 1、电…

程序员应该如何学习算法?

算法不是纯粹拼智商的&#xff0c;初学者不要上来直接撸《算法导论》&#xff01;这是血泪 建议一&#xff1a;首先你得会一门程序设计语言 建议二&#xff1a;基础知识&#xff0c;数据结构&#xff0c;推荐大家看一下《大话数据结构》这本书&#xff0c;这本书看过感觉&…

华为OD机试用Python实现 -【连续字母长度 or 求第 K 长的字符串长度】 | 2023.Q1 A卷

华为OD机试题 本篇题目:连续字母长度 or 求第 K 长的字符串长度题目输入描述输出描述示例一输入输出说明示例二输入输出说明示例三输入输出说明Code代码编写逻辑最近更新的博客 华为od 2023 | 什么是华为od,od

zookeeper使用场景实战

ZK java客户端 zk官方客户端没有和服务端分离&#xff0c;同一个jar文件&#xff0c;我们直接引入zk的maven即可。注意版本匹配兼容 Curator curator java语言编程的zk客户端框架&#xff0c;curator项目是现在zk客户端中使用最多。 将我们平时使用的zk服务开发进行了封装&a…

【Linux】进程状态(阻塞、挂起、僵尸进程)

文章目录1 阻塞与挂起1.1 阻塞1.2 挂起2 进程状态前言&#xff1a; 当我们在Windows下双击运行一个程序&#xff0c;或是在Linux下通过 ./ 加载运行一个程序&#xff0c;是否就代表对应的进程就一直处在运行状态呢&#xff1f;其实不然&#xff0c;一个进程有许多不同的状态。当…

科技和女性的今天,《赛博格宣言》半个世纪前就预言了

近几年&#xff0c;我们团队在实地探访各行各业数字化时&#xff0c;格外关注女性工作者的存在&#xff0c;一个强烈感受是&#xff1a;和女性主义理论中说的一样&#xff0c;因为有了数字化技术&#xff0c;工作对于体力、精力等要求不再苛刻&#xff0c;岗位上的女员工就多了…

设计模式~门面(外观)模式(Facade)-08

目录 &#xff08;1&#xff09;优点 &#xff08;2&#xff09;缺点 &#xff08;3&#xff09;使用场景 &#xff08;4&#xff09;注意事项&#xff1a; &#xff08;5&#xff09;应用实例&#xff1a; &#xff08;6&#xff09;源码中的经典应用 代码 外观模式&am…

类和对象万字详解

目录 一、面向对象与面向过程的区别 面向过程&#xff1a; 面向对象&#xff1a; 二、类的引入 class与struct爱恨情仇 class的语法 类的定义&#xff1a; 类的限定访问符 类的实例化 类对象模型 this指针的应用 三、封装 四、类的六个默认成员函数 构造函数 再谈…

基于NMOSFET的电平转换电路设计

一、概述&#xff1a; 在单片机系统中&#xff0c;5V、3.3V是芯片常用的电平。而在传输协议中(如IIC、SPI等协议)&#xff0c;存在芯片与芯片的高电平和低电平定义的范围不一样&#xff0c;所以需要存在一个电平转换电路&#xff0c;来使芯片与芯片之间顺利的传输。 二、前置…

JDK动态代理(tedu)(内含源代码)

JDK动态代理&#xff08;tedu&#xff09;&#xff08;内含源代码&#xff09; 源代码下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87546187 目录JDK动态代理&#xff08;tedu&#xff09;&#xff08;内含源代码&#xff09;源代码下载链接…