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

设计模式之六大设计原则

文章目录

  • 高内聚低耦合
  • 设计原则
    • 开闭原则
    • 单一职责原则
    • 里氏代换原则
    • 依赖倒置原则
    • 迪米特原则
    • 接口隔离原则

高内聚低耦合

提高代码的可读性、可维护性和可扩展性,降低开发和维护的成本,并减少系统的风险

内聚:

内聚表示一个模块内部各个元素之间相互关联的程度。

高内聚意味着模块内部的功能紧密相关,所有部分都共同完成一个特定的任务或功能。

低内聚则意味着模块内部包含多种不相关的功能。

耦合:

耦合表示不同模块之间相互依赖的程度。

高耦合意味着模块之间相互依赖紧密,一个模块的变化会影响到其他模块。

低耦合则意味着模块之间相对独立,一个模块的变化对其他模块的影响较小。

举个例子:

汽车是由许多不同的部件组成的,比如发动机、轮胎、刹车等等。

在高内聚低耦合的设计中,每个部件都应该专注于自己的功能,同时尽可能减少与其他部件之间的依赖关系。

高内聚意味着每个部件都应该有一个清晰的责任和功能。发动机的责任是提供动力,而刹车系统的责任是提供制动。这样设计的好处是,每个部件都可以独立工作,而不需要过多依赖其他部件的内部细节。

低耦合意味着部件之间的相互依赖应该尽可能减少。刹车系统不需要知道发动机如何工作,它只需要知道何时需要制动。这样设计的好处是,如果需要更改或替换一个部件,不会对其他部件产生太大影响,因为它们之间的依赖关系很少。

高内聚:意味着一个类或模块的内部元素(包括变量、方法和属性)应该紧密相关,并且共同服务于一个明确且集中的目的。

低耦合:模块或类之间的依赖关系应该尽可能少

  • 内紧(高内聚):程序内的模块或类应该紧密相关,形成一个高效的功能单元。
  • 外松(低耦合):程序之间的模块或类应该尽可能不关联,各自实现各自的功能。

设计原则

在进行软件系统设计时所要遵循的一些经验准则,应用该准则的目的通常是为了避免某些经常出现的设计缺陷,提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性

包括单一职责原则,开放封闭原则,里氏替换原则,依赖倒置原则,迪米特原则,接口隔离原则

开闭原则

(Open-Closed Principle, OCP)

定义:类,模块,函数等应该是可以拓展的,但是不可修改

对扩展开放,对修改关闭。

我们需要使用抽象实现(接口和抽象类)达到这样的效果。

因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。

而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。

举个例子

//AbstractSkin.java
public abstract class AbstractSkin {//显示皮肤public abstract void display();
}//DefaultSkin.java
public class DefaultSkin extends AbstractSkin{@Overridepublic void display() {System.out.println("默认皮肤");}
}//WhiteSkin.java
public class WhiteSkin extends AbstractSkin{@Overridepublic void display() {System.out.println("白色皮肤");}
}//SougouInput.java
public class SougouInput {private AbstractSkin skin;public AbstractSkin getSkin() {return skin;}//通过setSkin方法设置不同的皮肤实现public void setSkin(AbstractSkin skin) {this.skin = skin;}public void display() {skin.display();}
}//Client.java
public class Client {public static void main(String[] args) {//1.创建输入法对象SougouInput input = new SougouInput();//2.创建皮肤对象DefaultSkin skin = new DefaultSkin();//WhiteSkin skin = new WhiteSkin();//3.将皮肤设置到输入法中input.setSkin(skin);//4.显示皮肤input.display();}
}

SougouInput 类通过其 setSkin 方法允许添加不同类型的皮肤(如 DefaultSkinWhiteSkin),这体现了对扩展的开放性。

由于 SougouInput 类依赖于 AbstractSkin 抽象类而不是具体的皮肤实现类,因此添加新的皮肤类型只需要创建新的子类并实现 AbstractSkindisplay 方法,而不需要修改 SougouInput 类的代码。这体现了对修改的封闭性。

皮肤的类不用修改,再创建新皮肤,让他继承AbstractSkin抽象类,在客户端类代码进行修改即可。

作用

  1. 提高软件的可维护性
  2. 增强软件的扩展性
  3. 减少代码的耦合度

单一职责原则

(Single Responsibility Principle,SRP)

定义:一个类(或模块、函数等)应该只有一个引起它变化的原因。换句话说,一个类应该只负责一个功能领域中的相关职责,或者变化的原因应该只有一个。

下面是不符合单一职责原则的例子:

public class TelPhone {public void Dial(String phone_number){System.out.println("给"+phone_number+"打电话");}public void HangUp(String phone_number) {System.out.println("挂断" + phone_number + "打电话");}public void SendMessage(String message) {System.out.println("发送" + message);}public void ReceiveMessage(String message) {System.out.println("接收" + message);}
}

可能发生的变化:

  1. 内部的变化,如果 TelPhone 类中的任何一个方法发生改变,,都需要修改TeIPhone、由于它负责了多个职责,所以一个职责的变化可能会导致其他无关职责的代码也需要被修改或重新测试。

  2. 外部的变化,如果TeIPhone要添加新的的方法,需要修改TeIPhone

为了符合单一职责原则,我们可以做出如下修改:

但中间的依然不符合,存在一个以上引起类变化的原因,所以我们可以考虑最右边的,一个类中只有一个方法

yuanze2

但这样比较极端,会导致类的数量大幅增加,使得管理和维护代码变得复杂。

因此,我们通常要在单一职责原则和实际应用之间找到一个平衡点。

实现方式:

给每个方法,都提炼成一个接口,抽象成一种能力,然后分别写类,去实现接口,最终在TelPhone中只进行调用。

package com.feng.test01;interface Dialer {void Dial(String phoneNumber);
}interface Hanger {void HangUp();
}interface Sender {void SendMessage(String text);
}interface Receiver {void ReciveMessage(String text);
}class DialerImpl implements Dialer {public void Dial(String phoneNumber) {System.out.println("给 " + phoneNumber + " 打电话");}
}class HangerImpl implements Hanger {public void HangUp() {System.out.println("挂断电话");}
}class SenderImpl implements Sender {public void SendMessage(String text) {System.out.println("发送 " + text);}
}class ReceiverImpl implements Receiver {public void ReciveMessage(String text) {System.out.println("接收 " + text);}
}class TelPhone {private Dialer dialer;private Hanger hanger;private Sender sender;private Receiver receiver;public TelPhone(Dialer dialer, Hanger hanger, Sender sender, Receiver receiver) {this.dialer = dialer;this.hanger = hanger;this.sender = sender;this.receiver = receiver;}public void Dial(String phoneNumber) {dialer.Dial(phoneNumber);}public void HangUp() {hanger.HangUp();}public void SendMessage(String text) {sender.SendMessage(text);}public void ReciveMessage(String text) {receiver.ReciveMessage(text);}
}public class Main {public static void main(String[] args) {// 创建接口实现类实例Dialer dialer = new DialerImpl();Hanger hanger = new HangerImpl();Sender sender = new SenderImpl();Receiver receiver = new ReceiverImpl();// 创建 TelPhone 对象并使用其接口TelPhone telphone = new TelPhone(dialer, hanger, sender, receiver);// 电话呼叫操作telphone.Dial("123456789");telphone.HangUp();// 消息操作telphone.SendMessage("Hello, World!");telphone.ReciveMessage("Hi there!");}
}

好处:

  1. 提高代码的可读性,提高系统的可维护性。
  2. 降低类的复杂性,一个模块只负责一个职责,提高系统的可扩展性和可维护性。
  3. 降低变更引起的风险。变更是不然的,如果单一职责做得好,当修改一个功能的时候可以显著的降低对另一个功能的影响。

里氏代换原则

(Liskov Substitution Principle,LSP)

里氏代换原则:任何基类可以出现的地方,子类一定可以出现。

通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。

要点:

功能保持:子类应当能够替代基类,并且子类对象应能代替基类对象使用,而不会导致程序运行出现问题。

行为一致:子类可以扩展基类的功能,但不能改变基类的功能。即子类的行为应该与基类保持一致

要求:

  1. 子类可以实现父类的抽象方法,但不要去覆盖(重写)父类的非抽象方法
  2. 子类可以增加自己特有的方法
  3. 当子类的方法重写父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松
  4. 当子类的方法实现父类的方法时(重写或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或与父类一样
package com.feng.test05;public abstract class Coder {public void coding() {System.out.println("我会敲代码!");}
}class JavaCoder extends Coder {public void play() {System.out.println("喜欢玩XXXX");}//重写了父类的非抽象方法public void coding() {System.out.println("我只会敲JAVA代码!");}
}

里氏代换原则指出,如果程序中的对象使用的是基类(父类)的话,那么无论是使用基类对象还是其子类对象,程序的行为都是一致的。

JavaCoder 替代 Coder 时,本来会敲很多代码,但现在只会敲JAVA了。

所以尽量不要去重写父类非抽象方法,不要改变父类原有的功能。

可以这样修改:

  1. 保留父类方法的行为,并且扩展子类方法的功能
    public void coding() {super.coding(); // 调用父类的coding方法System.out.println("我会敲JAVA代码!");}
  1. 或者再写一个javaCoding方法
  2. JavaCoderCoder的抽象父类People,把coding这一行为定义在People中,放弃JavaCoderCoder的继承关系

好处

  1. 开放性:是实现开放封闭原则的的具体手段之一
  2. 提高代码的可复用性

依赖倒置原则

(Dependence Inversion Principle,DIP)

定义:高层模块不应该依赖低层模块,两者都应该依赖抽象。抽象不应该依赖细节,细节应该依赖抽象。

旨在通过依赖于抽象而不是具体实现来降低系统的耦合度,提高系统的可维护性和可扩展性。

下面是一个违背依赖倒置原则的例子

package com.feng.test04;class FuelCar{public void run(){System.out.println("开的是燃油车");}
}class Driver{public void drive(FuelCar car) {car.run();}
}public class Client {public static void main(String[] args) {FuelCar fuelCar = new FuelCar();Driver xiaowang = new Driver();xiaowang.drive(fuelCar);}
}

Driver 类直接依赖于 FuelCar 类,而没有使用抽象,违反了依赖倒置原则。

如果我们想要支持其他类型的车辆,比如电动车或者公交车,就需要修改 Driver 类,这样会增加代码的耦合度和维护成本。

高层模块(Driver)不应该直接依赖于低层模块(FuelCar),而是应该依赖于抽象。在这个例子中,Driver 类应该依赖于一个抽象的 ICar 接口,而不是具体的 FuelCar 类。

我们可以引入一个接口来表示所有类型的车,并让FuelCar实现这个接口。然后,Driverdrive方法可以接受任何实现了ICar接口的对象作为参数。

package com.feng.test04after;interface ICar {void run();
}class FuelICar implements ICar {@Overridepublic void run() {System.out.println("开的是燃油车");}
}class ElectricICar implements ICar {@Overridepublic void run() {System.out.println("开的是电车");}
}class Driver {public void drive(ICar car) {car.run();}
}public class Client {public static void main(String[] args) {ICar su7 = new ElectricICar();ICar benz = new FuelICar();Driver driver = new Driver();driver.drive(su7);driver.drive(benz);}
}

作用

  1. 提高代码的可维护性
  2. 降低代码的耦合度
  3. 提高系统的可扩展性

迪米特原则

(Law of Demeter,LoD)

也称为最少知识原则:一个对象应该对其他对象有最少的了解。

定义:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用

一个对象应该对其他对象知之甚少,只与“朋友”通信,而不与“陌生人”直接通信。

  • 租房者-中介-房东
  • 要做软件的公司-软件公司-软件工程师

这里的“朋友”指的是当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等

这些对象同当前对象存在关联、依赖、聚合或组合关系,可以直接访问这些对象的方法。

下面是一个

import java.util.ArrayList;
import java.util.List;class Product {private String name;private double price;public Product(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public double getPrice() {return price;}
}// 购物车类
class ShoppingCart {private List<Product> products;public ShoppingCart() {this.products = new ArrayList<>();}// 添加商品到购物车public void addProduct(Product product) {products.add(product);}// 打印购物车中的商品信息public void printCart() {for (Product product : products) {System.out.println("商品:" + product.getName() + " 价格:" + product.getPrice());}}
}public class Test {public static void main(String[] args) {Product product1 = new Product("banana", 2);Product product2 = new Product("apple", 5);ShoppingCart cart = new ShoppingCart();cart.addProduct(product1);cart.addProduct(product2);cart.printCart();}
}

ShoppingCart 类对 Product 类的了解仅限于 getNamegetPrice 方法,这是符合迪米特原则的。ShoppingCart 不需要知道 Product 类的内部实现细节,也不需要与其他任何与 Product 类相关的“陌生人”对象进行交互。

好处:

  1. 降低耦合性
  2. 提高模块独立性
  3. 增强系统的可维护性

接口隔离原则

(Interface Segregation Principle,ISP)

定义:一个类对另一个类的依赖应该建立在最小的接口上。

建立单一接口,不要建立庞大臃肿的接口;尽量细化接口,接口中的方法尽量少。

也就是说,我们要为各个类建立专用的接口,而不要试图建立一个很庞大的接口件所有依赖它的类通用。

下面是一个不符合接口隔离原则的例子:

package com.feng.test03;interface Device {String getCpu();String getType();String getMemory();
}
class computer implements Device{@Overridepublic String getCpu() {return "i7";}@Overridepublic String getType() {return "笔记本电脑";}@Overridepublic String getMemory() {return "16GB";}
}class fan implements Device{@Overridepublic String getCpu() {return null; //不需要的方法}@Overridepublic String getType() {return "电风扇";}@Overridepublic String getMemory() {return null; //不需要的方法}
}

虽然定义了一个Device接口,但是由于此接口的粒度不够细化,类依赖于不需要的方法。虽然比较契合电脑这种设备,但是不适合风扇,要对其进行更细粒度的划分。

接口的粒度:描述了接口所提供功能的大小和复杂度

下面是一个符合接口隔离原则粒度更细的代码:

// 通用设备接口  
interface GenericDevice {  String getType();  
}  // 电脑设备接口  
interface ComputerDevice extends GenericDevice {  String getCpu();  String getMemory();  
}  // 风扇设备接口  
interface FanDevice extends GenericDevice {void adjustSpeed(int speed);  
}  

注意:

  1. 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计的灵活性;但是如果过小,则会造成接口数量过多,使设计复杂化。所以,接口的大小一定要适度。
  2. 为依赖接口的类定制服务,只暴露给调用的类需要的方法,不需要的方法则隐藏起来只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
  3. 提高内聚,减少对外交互。接口方法尽量少用public修饰。接口是对外的承诺,承诺越少,对系统的开发越有利,变更风险也会越少。

作用

  1. 降低耦合性
  2. 提高灵活性
  3. 增强可维护性

  1. 开闭原则:抽象架构,扩展实现
  2. 单一职责:一个类和方法只做一件事
  3. 里氏替换: 多态,子类可扩展父类
  4. 依赖倒置:细节依赖抽象,下层依赖上层
  5. 接口隔离:建立单一接口
  6. 迪米特原则:最少知道,降低耦合


参考:

  1. 14.设计模式-设计原则(依赖倒转原则概述和案例)
  2. 【设计模式】六大原则详解
  3. 六大设计原则超详细介绍
  4. 面向对象设计原则


如有错误烦请指正。

感谢您的阅读

相关文章:

设计模式之六大设计原则

文章目录 高内聚低耦合设计原则开闭原则单一职责原则里氏代换原则依赖倒置原则迪米特原则接口隔离原则 高内聚低耦合 提高代码的可读性、可维护性和可扩展性&#xff0c;降低开发和维护的成本&#xff0c;并减少系统的风险 内聚&#xff1a; 内聚表示一个模块内部各个元素之间…...

【iOS】UI学习(一)

UI学习&#xff08;一&#xff09; UILabelUIButtonUIButton事件 UIViewUIView对象的隐藏UIView的层级关系 UIWindowUIViewController定时器与视图对象 UISwitch UILabel UILabel是一种可以显示在屏幕上&#xff0c;显示文字的一种UI。 下面使用代码来演示UILabel的功能&#…...

如何使用Vue和Markdown实现博客功能

创建Vue项目和安装依赖 npm install -g @vue/cli vue create vue-blog cd vue-blog npm install vue-markdown-loader --save-dev配置Vue项目以解析Markdown 在 vue.config.js 文件中添加以下配置: module.exports = {chainWebpack: config => {config...

1初识C#

1、Console安慰 Console.WriteLine("Hello, world!"); // 输出 "Hello, world!" 并换行 Console.WriteLine(123.45); // 输出数字 123.45 并换行 Console.WriteLine("Name: " name); // 输出 "Name: [变量name的值]" 并换行 2、 C…...

查询指定会话免打扰

查询指定用户&#xff08;requestId) 为指定会话&#xff08;targetId&#xff09;的设置的免打扰状态。 提示 该设置为用户级别设置。对应的设置接口详见设置指定会话免打扰。 请求方法 POST&#xff1a; https://数据中心域名/conversation/notification/get.json 频率限…...

Linux-命令

添加权限方法及注意事项: 字母权限法很灵活,无论目录还是文件都可以随意添加删除超级权限 chmod us ... 添加SUID chmod gs ... 添加SGID chmod s ...同时添加SUID和SGID chmod -s ...同时删除SUID和SGID chmod ot ...添加Sticky chmod t ...同上 数字权限表示法添加/删除…...

STM32读写内部FLASH读取芯片id

文章目录 读写内部Flash接线程序编写测试效果补充 读取芯片id代码编写 读写内部Flash 接线 程序编写 首先使用ThisFlash.c来写入flash的基本操作&#xff0c;写入、读取、擦除&#xff0c;然后使用Store.c配合数组来进行主存与flash的交互 ThisFlash.c #include "stm32…...

前端npm打包及报错解决

前端npm install 安装node 下载地址 https://nodejs.org/en/download/prebuilt-binaries 配置环境变量 wget https://nodejs.org/dist/v14.21.3/node-v14.21.3-linux-x64.tar.xz tar xf node-v14.21.3-linux-x64.tar.xz -C /data/ vim /etc/profile export NODE_HOME/data/n…...

vbs执行报错vbs没有文件拓展,双击无法打开

如果看不到文件扩展名需要设置&#xff1a; 无法双击打开vbs 一般为注册表问题 解决办法 将下方代码保存为xxx.reg Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\.VBS] "VBSFile" [HKEY_CLASSES_ROOT.VBS\PersistentHandler] "{5e941d80-bf96-…...

超详细的前后端实战项目(Spring系列加上vue3)前端篇(二)(一步步实现+源码)

好了&#xff0c;兄弟们&#xff0c;继昨天的项目之后&#xff0c;开始继续敲前端代码&#xff0c;完成前端部分 昨天完成了全局页面的代码&#xff0c;和登录页面的代码&#xff0c;不过昨天的代码还有一些需要补充的&#xff0c;这里添加一下 内容补充&#xff1a;在调用登…...

【国产中颖】SH79F9202U单片机驱动LCD段码液晶学习笔记

1. 引言 因新公司之前液晶数显表产品单片机一直用的是 C51单片机(SH79F9202U9)&#xff0c;本人之前没有接触过这款单片机&#xff0c;为了维护老产品不得不重新研究研究这款单片机。 10位ADC LCD的增强型8051微控制器 SH79F9202是一种高速高效率8051可兼容单片机。在同样振…...

人工智能初识

&#x1f31e;欢迎来到人工智能基础的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f4c6;首发时间&#xff1a;&#x1f339;2024年5月1…...

【算法刷题day60】Leetcode:84. 柱状图中最大的矩形

文章目录 Leetcode 84. 柱状图中最大的矩形解题思路代码总结 草稿图网站 java的Deque Leetcode 84. 柱状图中最大的矩形 题目&#xff1a;84. 柱状图中最大的矩形 解析&#xff1a;代码随想录解析 解题思路 反方向接雨水。见上一篇文章 代码 class Solution {public int la…...

ThingsBoard物联网网关在智慧城市数据采集中的应用

智慧城市由监控中心、采集网关、前端采集设备、前端感应执行器组成。 为何选用ThingsBoard作为平台 监控中心为物联网平台&#xff0c;该平台包含云计算、大数据、人工智能、物联网、GIS、云安全等主要模块&#xff0c;具备数据采集、数据交换、超大规模计算、数据分析、数据应…...

Java中的打印流PrintStream 和 PrintWriter

PrintStream和PrintWriter在Java中都是用于打印输出的类&#xff0c;但它们之间存在一些明显的区别。以下是关于这两个类的详细解释和比较&#xff1a; PrintStream 基本特性 PrintStream是一个字节打印流&#xff0c;它继承自FilterOutputStream。 主要操作byte流&#xff0…...

【MATLAB源码-第217期】基于matlab的16QAM系统相位偏移估计HOS算法仿真,对比补偿前后的星座图误码率。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 高阶统计量&#xff08;HOS&#xff09;频偏估计算法 高阶统计量&#xff08;Higher Order Statistics, HOS&#xff09;频偏估计算法是一种先进的信号处理技术&#xff0c;广泛应用于现代数字通信系统中&#xff0c;以应对…...

C# CryptoStream流的详解与示例

在当今数字时代&#xff0c;数据安全变得越来越重要。保护敏感信息免受未授权访问是每个开发者的责任。在C#中&#xff0c;使用CryptoStream流可以方便地对数据进行加密和解密。本文将详细介绍C# CryptoStream库的用法、功能以及它如何对数据进行加密和解密。 一、CryptoStrea…...

Kubernetes 之 ReplicaSet

Kubernetes 之 ReplicaSet ReplicaSet 定义 ReplicaSet 是 Kubernetes 中的一种副本控制器&#xff0c;其主要作用是控制其管理的 Pod 的预设副本数量。它会持续监听这些 Pod 的运行状态&#xff0c;在Pod发生故障时执行重启策略&#xff0c;当 Pod 数量减少时会重新启动新的…...

转发和重定向

目录 是什么 转发&#xff08;Forwarding&#xff09; 概念 特点 实现方式 重定向&#xff08;Redirecting&#xff09; 概念 特点 实现方式 转发和重定向区别整理 转发和重定向的适用场景 转发&#xff08;Forwarding&#xff09; 重定向&#xff08;Redirect&am…...

源码部署ELK

目录 资源列表 基础环境 关闭防护墙 关闭内核安全机制 修改主机名 添加hosts映射 一、部署elasticsearch 修改limit限制 部署elasticsearch 修改配置文件 单节点 集群(3台节点集群为例) 启动 二、部署logstash 部署logstash 添加配置文件 启动 三、部署kiban…...

构造+模拟,CF1148C. Crazy Diamond

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1148C - Codeforces 二、解题报告 1、思路分析 题目提示O(5n)的解法了&#xff0c;事实上我们O(3n)就能解决&#xff0c;关键在于1&#xff0c;n的处理 我们读入数据a[]&#xff0c;代表初始数组…...

CAD二次开发(2)-将直线对象添加到CAD图形文件

1. 准备工作 创建一个类库项目&#xff0c;如下&#xff1a; 2. 分析Line对象 Line类的初始化方法和参数 using Autodesk.AutoCAD.DatabaseServices; Line line new Line();Line 继承Curve 继承Entity 继承DBObject 继承Drawable 继承RXObject 初始化方法有两个&#xf…...

代码随想录二刷 Day05 | 242.有效的字母异位词,349. 两个数组的交集,202. 快乐数,1. 两数之和,454.四数相加II,383. 赎金信

题目与题解 参考资料&#xff1a;哈希表理论基础 Tips&#xff1a; 一般哈希表都是用来快速判断一个元素是否出现集合里哈希表生成原理&#xff1a;先通过哈希函数将变量映射为hashcode&#xff0c;如果二者hashcode相同&#xff0c;再通过哈希碰撞方法&#xff08;拉链法&…...

2024年四川省三支一扶报名流程图解✅

2024年四川省三支一扶报名流程图解✅ &#x1f534;时间安排 1、报名时间&#xff1a;5月31日—6月4日17:00 2、资格初审时间&#xff1a;5月31日—6月5日17:00 3、准考证打印时间&#xff1a;6月25日—6月29日 4、笔试时间&#xff1a;6月30日 5、笔试成绩&#xff1a;7…...

js Dom基础

获取元素 1、getElementById() 通过id属性获取一个元素节点对象 <div id"div1"></div> <script> var div1 document.getElementById(div1) </script> 2、 getElementsByTagName()可以根据标签名来获取一组元素节点对象 这个方法会给我们返…...

pytest识别测试用例的机制以及和unittest的区别

pytest识别测试用例的机制 文件 以test_开头或以_test结尾的python文件&#xff0c;即test_xxx.py或xxx_test.py类&#xff0c;在第一点识别到的文件中的类&#xff0c;且满足一下任一条件&#xff1a; 1&#xff09;以Test_开头&#xff0c;且没有__init__()初始化函数的类&a…...

民国漫画杂志《时代漫画》第17期.PDF

时代漫画17.PDF: https://url03.ctfile.com/f/1779803-1248612629-85326d?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了&#xff0c;截止1937年6月战争来临被迫停刊共发行了39期。 ps:资源来源网络&#xff01;...

[AIGC] Spring Boot 2 自定义 Starter 指南

Spring Boot 包含一系列的 “Starter POMs”&#xff0c;它们都是一些方便的依赖描述符&#xff0c;你可以在你的应用中导入。在一些情况下&#xff0c;你可能想创建自己的自定义 starter。以下是创建自己的 Spring Boot Starter 的步骤。 文章目录 1. 创建基本的 Maven 项目2.…...

HCIP综合实验命令

目录 一、配置IP地址 二、配置DHCP 三、配置静态路由&#xff08;内网通&#xff09; 四、配置缺省路由 &#xff08;外网通&#xff09; 五、配置缺省 &#xff08;全网通&#xff09; 六、防环配置 七、配置远程登录 八、修改优先级 九、配置MP-GROUP 十、配置ppp进…...

JS移动端设置mouseover,mouseleave有效么

在移动设备的浏览器环境中&#xff0c;mouseover 和 mouseleave 事件的行为与桌面浏览器有所不同&#xff0c;主要是因为移动设备的交互方式主要是基于触摸的&#xff0c;而不是基于鼠标的。 在移动设备上&#xff0c;当用户触摸屏幕时&#xff0c;通常会触发 touchstart 事件…...