软件设计模式:六大设计原则
文章目录
- 前言
- 一、开闭原则
- 二、里氏替换原则
- 三、依赖倒转原则
- 四、接口隔离
- 五、迪米特法则
- 六、合成复用原则
- 总结
前言
在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据6条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。
六大设计原则:开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、迪米特原则、合成复用原则。
一、开闭原则
- 对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。
- 想要达到这样的效果,我们需要使用接口和抽象类。
- 因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。
下面以 搜狗输入法 的皮肤为例介绍开闭原则的应用。
【例】搜狗输入法 的皮肤设计。
分析:搜狗输入法 的皮肤是输入法背景图片、窗口颜色和声音等元素的组合。用户可以根据自己的喜爱更换自己的输入法的皮肤,也可以从网上下载新的皮肤。这些皮肤有共同的特点,可以为其定义一个抽象类(AbstractSkin),而每个具体的皮肤(DefaultSpecificSkin和HeimaSpecificSkin)是其子类。用户窗体可以根据需要选择或者增加新的主题,而不需要修改原代码,所以它是满足开闭原则的。而且就算是厂家上新皮肤,只需要继承抽象类实现就行。
代码实现:
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: AbstractSkin* @Description: 皮肤抽象类* @Date: 2023/12/19 21:34*/
public abstract class AbstractSkin {// 显示的方法public abstract void display();
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: SougouInput* @Description: 搜狗输入法* @Date: 2023/12/19 21:40*/
public class SougouInput {private AbstractSkin skin;public void setSkin(AbstractSkin skin) {this.skin = skin;}public void display() {skin.display();}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: DefaultSkin* @Description: 默认皮肤类* @Date: 2023/12/19 21:35*/
public class DefaultSkin extends AbstractSkin{@Overridepublic void display() {System.out.println("默认皮肤");}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: DragonSkin* @Description: 浩泽皮肤类* @Date: 2023/12/19 21:38*/
public class DragonSkin extends AbstractSkin{@Overridepublic void display() {System.out.println("浩泽皮肤");}
}
模拟用户选择使用皮肤:
public class Client {@Testpublic void testSkin(){SougouInput input = new SougouInput();// 选择皮肤DefaultSkin skin = new DefaultSkin();// 设置皮肤input.setSkin(skin);// 显示input.display();}
}
二、里氏替换原则
- 里氏代换原则是面向对象设计的基本原则之一。
- 里氏代换原则:任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
- 如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。
下面看一个里氏替换原则中经典的一个例子
【例】正方形不是长方形。
在数学领域里,正方形毫无疑问是长方形,它是一个长宽相等的长方形。所以,我们开发的一个与几何图形相关的软件系统,就可以顺理成章的让正方形继承自长方形。
代码实现:
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Rectangle* @Description: 长方形类* @Date: 2023/12/20 10:20*/
public class Rectangle {private double length;private double width;public double getLength() {return length;}public void setLength(double length) {this.length = length;}public double getWidth() {return width;}public void setWidth(double width) {this.width = width;}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Square* @Description: 正方形类* @Date: 2023/12/20 10:21*/
public class Square extends Rectangle {public void setWidth(double width) {super.setLength(width);super.setWidth(width);}public void setLength(double length) {super.setLength(length);super.setWidth(length);}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: RectangleDemo* @Description: 变换长宽测试* @Date: 2023/12/20 10:24*/
public class RectangleDemo {public static void main(String[] args) {Rectangle r = new Rectangle();r.setLength(20);r.setWidth(10);resize(r);printLengthAndWithWidth(r);Square s = new Square();s.setLength(10);resize(s);printLengthAndWithWidth(r);}public static void resize(Rectangle rectangle) {while (rectangle.getWidth() <= rectangle.getLength()) {rectangle.setWidth(rectangle.getWidth() + 1);}}public static void printLengthAndWithWidth(Rectangle rectangle) {System.out.println("Length:"+rectangle.getLength());System.out.println("Width:"+rectangle.getWidth());}
}
运行发现正方形调用的方法一直没在有显示,仔细分析:那是因为正方形就一个边长,所以我们创建正方形类继承长方形时,让其长宽都等于一个值(正方形的边长),所以在测试类里,变换边长的函数里的while内的判断条件始终是true(rectangle.getWidth() == rectangle.getLength())),所以一直在循环里出不来。
正方形的宽度和长度都在不断增长,代码会一直运行下去,直至系统产生溢出错误。所以,普通的长方形是适合这段代码的,正方形不适合。
我们得出结论:在resize方法中,Rectangle类型的参数是不能被Square类型的参数所代替,如果进行了替换就得不到预期结果。因此,Square类和Rectangle类之间的继承关系违反了里氏代换原则,它们之间的继承关系不成立,正方形不是长方形。
如何改进呢?此时我们需要重新设计他们之间的关系。抽象出来一个四边形接口(Quadrilateral),让Rectangle类和Square类实现Quadrilateral接口:
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Quadrilateral* @Description: 四边形接口* @Date: 2023/12/20 10:34*/
public interface Quadrilateral {double getLength();double getWidth();
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Rectangle* @Description: 长方形类* @Date: 2023/12/20 10:38*/
public class Rectangle implements Quadrilateral{private double length;private double width;public void setLength(double length) {this.length = length;}public void setWidth(double width) {this.width = width;}@Overridepublic double getLength() {return length;}@Overridepublic double getWidth() {return width;}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Square* @Description: 正方形类* @Date: 2023/12/20 10:36*/
public class Square implements Quadrilateral{private double side;public double getSide() {return side;}public void setSide(double side) {this.side = side;}@Overridepublic double getLength() {return side;}@Overridepublic double getWidth() {return side;}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: RectangleDemo* @Description: TODO(描述)* @Date: 2023/12/20 10:39*/
public class RectangleDemo {public static void main(String[] args) {Rectangle r = new Rectangle();r.setLength(20);r.setWidth(10);resize(r);printLengthAndWidth(r);}public static void resize(Rectangle rectangle) {while (rectangle.getWidth() <= rectangle.getLength()) {rectangle.setWidth(rectangle.getWidth() + 1);}}public static void printLengthAndWidth(Quadrilateral quadrilateral) {System.out.println("Length:" + quadrilateral.getLength());System.out.println("Width:" + quadrilateral.getWidth());}
}
这时square对象是无法调用的,resize只能传Rectangle类型
三、依赖倒转原则
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
下面看一个例子来理解依赖倒转原则
【例】组装电脑
现要组装一台电脑,需要配件cpu,硬盘,内存条。只有这些配置都有了,计算机才能正常的运行。选择cpu有很多选择,如Intel,AMD等,硬盘可以选择希捷,西数等,内存条可以选择金士顿,西部数据等。
分析:上面的结构可以看到组装一台电脑,但是似乎组装的电脑的cpu只能是Intel的,内存条只能是金士顿的,硬盘只能是希捷的,这对用户肯定是不友好的,用户有了机箱肯定是想按照自己的喜好,选择自己喜欢的配件。
根据依赖倒转原则进行改进:
代码我们只需要修改Computer类,让Computer类依赖抽象(各个配件的接口),而不是依赖于各个组件具体的实现类。
代码实现:
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Computer* @Description: 电脑类* @Date: 2023/12/20 16:26*/
public class Computer {private HardDisk hardDisk;private Cpu cpu;private Memery memery;public HardDisk getHardDisk() {return hardDisk;}public void setHardDisk(HardDisk hardDisk) {this.hardDisk = hardDisk;}public Cpu getCpu() {return cpu;}public void setCpu(Cpu cpu) {this.cpu = cpu;}public Memery getMemery() {return memery;}public void setMemery(Memery memery) {this.memery = memery;}public void run(){System.out.println("计算机开始工作");cpu.run();memery.save();String data = hardDisk.get();System.out.println("从硬盘中获取的数据为:" + data);}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Cpu* @Description: Cpu接口* @Date: 2023/12/20 16:18*/
public interface Cpu {public void run();
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: HardDisk* @Description: 硬盘接口* @Date: 2023/12/20 16:17*/
public interface HardDisk {public void save(String data);public String get();
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Memery* @Description: 内存条接口* @Date: 2023/12/20 16:19*/
public interface Memery {public void save();
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: IntelCpu* @Description: 英特尔 Cpu* @Date: 2023/12/20 16:23*/
public class IntelCpu implements Cpu{@Overridepublic void run() {System.out.println("使用Intel处理器");}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: KingstonMemory* @Description: 金士顿内存类* @Date: 2023/12/20 16:25*/
public class KingstonMemory implements Memery{@Overridepublic void save() {System.out.println("使用金士顿作为内存条");}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: XijieHardDisk* @Description: 希捷硬盘类* @Date: 2023/12/20 16:19*/
public class XijieHardDisk implements HardDisk{@Overridepublic void save(String data) {System.out.println("使用希捷硬盘存储数据:" + data);}@Overridepublic String get() {System.out.println("使用希捷硬盘取数据");return "数据";}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: TestComputer* @Description: 电脑测试类* @Date: 2023/12/20 16:28*/
public class TestComputer {public static void main(String[] args) {Computer computer = new Computer();computer.setCpu(new IntelCpu());computer.setHardDisk(new XijieHardDisk());computer.setMemery(new KingstonMemory());computer.run();}}
四、接口隔离
客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。
下面看一个例子来理解接口隔离原则
【例】安全门案例
我们需要创建一个浩泽品牌的安全门,该安全门具有防火、防水、防盗的功能。可以将防火,防水,防盗功能提取成一个接口,形成一套规范。类图如下:
上面的设计我们发现了它存在的问题,黑马品牌的安全门具有防盗,防水,防火的功能。现在如果我们还需要再创建一个传智品牌的安全门,而该安全门只具有防盗、防水功能呢?很显然如果实现SafetyDoor接口就违背了接口隔离原则,那么我们如何进行修改呢?看如下类图:
代码实现:
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: AntiTheft* @Description: 防盗功能接口* @Date: 2023/12/20 16:33*/
public interface AntiTheft {public void antiTheft();
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Fireproof* @Description: 防火功能接口* @Date: 2023/12/20 16:34*/
public interface Fireproof {public void fireproof();
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Waterproof* @Description: 防水功能接口* @Date: 2023/12/20 16:35*/
public interface Waterproof {public void wateproof();
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: DragonSafetyDoor* @Description: 龙牌安全门* @Date: 2023/12/20 16:36*/
public class DragonSafetyDoor implements AntiTheft,Fireproof,Waterproof{@Overridepublic void antiTheft() {System.out.println("防盗");}@Overridepublic void fireproof() {System.out.println("防火");}@Overridepublic void wateproof() {System.out.println("防水");}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: HaozeSafeDoor* @Description: 浩泽牌安全门* @Date: 2023/12/20 16:38*/
public class HaozeSafeDoor implements AntiTheft,Fireproof{@Overridepublic void antiTheft() {System.out.println("防盗");}@Overridepublic void fireproof() {System.out.println("防火");}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: TestDoor* @Description: TODO(描述)* @Date: 2023/12/20 16:38*/
public class TestDoor {public static void main(String[] args) {HaozeSafeDoor haozeSafeDoor = new HaozeSafeDoor();DragonSafetyDoor dragonSafetyDoor = new DragonSafetyDoor();haozeSafeDoor.antiTheft();haozeSafeDoor.fireproof();dragonSafetyDoor.antiTheft();dragonSafetyDoor.fireproof();dragonSafetyDoor.wateproof();}
}
五、迪米特法则
迪米特法则又叫最少知识原则。
- 只和你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。
- 其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
- 迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。
下面看一个例子来理解迪米特法则
【例】明星与经纪人的关系实例
明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如和粉丝的见面会,和媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则。
类图如下:
代码实现:
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Agent* @Description: 经纪人类* @Date: 2023/12/20 21:11*/
public class Agent {private Start start;private Fans fans;private Company company;public void setStart(Start start) {this.start = start;}public void setFans(Fans fans) {this.fans = fans;}public void setCompany(Company company) {this.company = company;}public void meeting(){System.out.println(fans.getName() + "与明星" + start.getName() + "见面了。");}public void business() {System.out.println(company.getName() + "与明星" + start.getName() + "洽谈业务。");}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Company* @Description: 公司类* @Date: 2023/12/20 21:10*/
public class Company {private String name;public Company(String name) {this.name = name;}public String getName() {return name;}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Fans* @Description: 粉丝类* @Date: 2023/12/20 21:09*/
public class Fans {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Start* @Description: 明星类* @Date: 2023/12/20 21:08*/
public class Start {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}
/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: Test* @Description: 测试类* @Date: 2023/12/20 21:15*/
public class Test {public static void main(String[] args) {Fans fans = new Fans();fans.setName("武汉粉丝团");Start start = new Start();start.setName("浩泽");Company company = new Company("华中经济公司");Agent agent = new Agent();agent.setStart(start);agent.setCompany(company);agent.setFans(fans);agent.meeting();agent.business();}
}
六、合成复用原则
-合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
通常类的复用分为继承复用和合成复用两种。
继承复用虽然有简单和易实现的优点,但它也存在以下缺点:
- 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
- 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
- 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点:
- 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
- 对象间的耦合度低。可以在类的成员位置声明抽象。
- 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
下面看一个例子来理解合成复用原则
【例】汽车分类管理程序
汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。类图如下:
从上面类图我们可以看到使用继承复用产生了很多子类,如果现在又有新的动力源或者新的颜色的话,就需要再定义新的类。我们试着将继承复用改为聚合复用看一下。
总结
以上就是软件设计模式六大设计原则的讲解。
相关文章:

软件设计模式:六大设计原则
文章目录 前言一、开闭原则二、里氏替换原则三、依赖倒转原则四、接口隔离五、迪米特法则六、合成复用原则总结 前言 在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据6条原则来开发程序&am…...
Unity闪屏Logo去除
1.新建一个C#脚本,命名为 “SkipSplashScreen” (代码如下)。 using System.Collections; using System.Collections.Generic; using System; using UnityEngine; using UnityEngine.UI;#if !UNITY_EDITOR using UnityEngine; using UnityEn…...

Git账户密码http方式的配置
Git账户密码http方式的配置 入门 git在提交时每次都需要输入密码和账号信息,可以将账号和密码进行持久化存储, 当git push的时候输入一次用户名和密码就会被记录, 不需要每次输入,提高效率,进行一下配置࿱…...

【JUC】三十二、邮戳锁StampedLock
文章目录 1、邮戳锁2、锁饥饿问题的解决思路3、邮戳锁的特点4、代码演示:邮戳锁的传统读写用法5、代码演示:邮戳锁之乐观读6、邮戳锁的缺点7、终章回顾 前面提到了从无锁 ⇒ 独占锁 ⇒ 读写锁,但读写锁存在写锁饥饿的情况。 📕【读…...

城市里的“蛋壳运动空间”
近年来,秉承"发展群众体育,服务健康中国”的理念,全国各地持续推进全民健身与全民健康的融合发展。越来越多的口袋公园、户外运动设施出现在城市各个角落,一定程度上提升了全民运动的便利性和幸福感。 但是,遇到…...

Linux宝塔面板本地部署Discuz论坛发布到公网访问【无需公网IP】
文章目录 前言1.安装基础环境2.一键部署Discuz3.安装cpolar工具4.配置域名访问Discuz5.固定域名公网地址6.配置Discuz论坛 前言 Crossday Discuz! Board(以下简称 Discuz!)是一套通用的社区论坛软件系统,用户可以在不需要任何编程的基础上&a…...
Android Canvas状态save与restore,Kotlin
Android Canvas状态save与restore,Kotlin private fun f1() {val bitmap BitmapFactory.decodeResource(resources, R.mipmap.pic).copy(Bitmap.Config.ARGB_8888, true)val canvas Canvas(bitmap)val paint Paint(Paint.ANTI_ALIAS_FLAG)paint.color Color.RED…...

python爬取网页图片并下载
python爬取网页图片并下载之GET类型 准备工作 【1】首先需要准备好pycharm,并且保证环境能够正常运行 【2】安装request模块 pip install requestsimport request导入request内置模块 【3】安装lxml模块 pip install lxmlfrom lxml import etree导入lxml.etre…...

亚马逊prime会员日活动是免费的吗?prime day怎么选产品促销?——站斧浏览器
亚马逊prime会员日活动是免费的吗? 实际上,亚马逊prime会员日活动并不是免费的。亚马逊prime会员日是亚马逊推出的一项会员特权服务,只有成为亚马逊prime会员的消费者才能享受该项服务。而成为亚马逊prime会员需要支付一定的费用,…...

二叉树题目:输出二叉树
文章目录 题目标题和出处难度题目描述要求示例数据范围 前言解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题:输出二叉树 出处:655. 输出二叉树 难度 6 级 题目描述 要求 给定二叉树的根结点 root \textt…...
apache poi_5.2.5 实现对表格单元格的自定义变量名进行图片替换
apache poi_5.2.5 实现对表格单元格的自定义变量名进行图片替换 实现思路 1.首先定位到自定义变量名 2.然后先清除自定义变量名,可利用setText(null,0)来清除 3.在自定义变量名的位置添加图片,使用下面的代码 4.对于图片布局有要求的,利用C…...

Kafka--Kafka日志索引详解以及生产常见问题分析与总结
一、Kafka的Log日志梳理 这一部分数据主要包含当前Broker节点的消息数据(在Kafka中称为Log日志)。这是一部分无状态的数据,也就是说每个Kafka的Broker节点都是以相同的逻辑运行。这种无状态的服务设计让Kafka集群能够比较容易的进行水平扩展。比如你需要用一个新…...

Vue3-23-组件-依赖注入的使用详解
什么是依赖注入 个人的理解 : 依赖注入,是在 一颗 组件树中,由 【前代组件】 给 【后代组件】 提供 属性值的 一种方式 ;这种方式 突破了 【父子组件】之间通过 props 的方式传值的限制,只要是 【前代组件】提供的 依…...

css 美化滚动条
当div内容溢出容器定义的高度时,滚动条显示,并美化默认的滚动条样式 div 容器 <divclass"content">内容 </div>css 样式 /* 问话区域 滚动条 */ .content {overflow: auto;height: 662px;padding: 25px;scrollbar-width: thin; /* 设置滚动条宽度 */bo…...
Tomcat介绍及使用:构建强大的Java Web应用服务器
引言: 在现代软件开发中,Web应用已经成为了不可或缺的一部分。而为了构建高效、稳定的Web应用服务器,选择合适的工具和技术至关重要。Tomcat作为一款开源的Java Web应用服务器,凭借其丰富的功能和灵活的配置,成为了开发…...

怎么定义一套完成标准的JAVA枚举类型
一、背景 在java代码中,接口返回有各种各样的状态,比如400 401 200 500 403等常见的http状态码,也有我们自定义的很多业务状态码。如果系统比较复杂,制定一套完整的标准的状态码是非常有必要的,这样比较方面BUG排查。…...

Apache Seatunnel本地源码构建编译运行调试
Apache Seatunnel本地源码构建编译运行调试 文章目录 1. 环境准备1.1 Java环境1.2 Maven1.3 IDEA1.4 Docker环境1.5 Mysql8.0.281.6 其它环境准备 2. 源码包下载3. idea项目配置3.1 项目导入3.2 maven配置3.3 项目JDK配置3.4 项目启动参数配置3.4.1 seatunnel项目启动参数配置3…...

构建高效持久层:深度解析 MyBatis-Plus(02)
目录 引言1. 逻辑删除1.1 概述1.2 逻辑删除的优势1.3.为什么使用逻辑删除1.4 综合案例 2. 乐观锁和悲观锁2.1.什么是乐观锁和悲观锁2.2.乐观锁和悲观锁的区别2.3.综合案例 3. 分页插件总结 引言 在现代软件开发中,数据库操作是不可或缺的一环。为了提高系统的性能、…...

Gitlab仓库推送到Gitee仓库的一种思路
文章目录 Gitlab仓库推送到Gitee仓库的一种思路1、创建Gitee的ssh公钥(默认已有Gitlab的ssh公钥)2、添加Gitlab远程仓库地址3、添加Gitee远程仓库地址4、拉取Gitlab远程仓库指定分支到本地仓库指定分支(以test分支为例)5、推送本地…...

快速能访问服务器的文件
1、背景 访问ubuntu上的文件 2、方法 python3 -m http.server 8081 --directory /home/ NAS 共享访问协议 — NFS、SMB、FTP、WebDAV 各有何优势?http://1 Ubuntu 搭建文件服务器(Nginx)...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...