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

设计模式の软件设计原则

文章目录

  • 前言
  • 一、聚合&组合&继承&依赖
    • 1.1、继承
    • 1.2、组合
    • 1.3、聚合
    • 1.4、依赖
  • 二、单一职责原则
    • 2.1、单一职责原则反面案例
    • 2.2、单一职责原则反面案例的改进
  • 三、接口隔离原则
    • 3.1、接口隔离原则反面案例
    • 3.2、接口隔离原则反面案例的改进
  • 四、依赖倒转原则
    • 4.1、依赖倒转原则反面案例
    • 4.1、依赖倒转原则反面案例的改进
  • 五、里氏替换原则
    • 5.1、里氏替换原则反面案例
    • 5.2、里氏替换原则反面案例的改进
  • 六、开闭原则
    • 6.1、开闭原则反面案例
    • 6.2、开闭原则反面案例的改进
  • 七、迪米特原则
    • 7.1、迪米特原则反面案例
    • 7.2、迪米特原则反面案例的改进
  • 八、合成复用原则
    • 8.1、合成复用原则反面案例
    • 8.2、合成复用原则反面案例的改进


前言

  本篇是设计模式专题笔记的前置理论篇,介绍软件设计的七大原则:单一职责原则、接口隔离原则、依赖倒转原则、里氏替换原则、开闭原则、迪米特法则、合成复用原则。上述七大原则,既是软件设计的原则,也是总结出23种设计模式的核心思想。不仅是学习设计模式的前置理论,更是对Java面向对象三大特征的延伸扩展。


一、聚合&组合&继承&依赖

  在介绍七大原则之前,首先需要明白什么是聚合、组合、依赖、继承。

1.1、继承

  继承是面向对象的三大特征之一。A类继承了B类,就拥有了B类的一切特征。如果A类重写了B类的某个方法,该方法在A中会以重写的生效。很多中间件、框架的自定义配置,即是用自定义类继承框架中原有的父类,重写其中的方法。
  但是使用继承,很大程度上也会破坏七大原则,应该慎用继承。个人理解,何时使用继承,最简单的就是,学生类可以继承人类,猫不能继承人类。即:没有共性的类之间,不要使用继承,如果两者之间只是代码相似,可以用组合代替继承。

1.2、组合

  组合是一种强“拥有”关系,表示一个类完全负责另一个类的生命周期,包含的对象依附于整体对象存在。这种关系通常用来描述“整体-部分”关系,且部分对象不可分离。整体销毁时部分对象也随之销毁:(因为部分对象是在整体对象中创建的。)

class Engine {public void start() {System.out.println("Engine starts.");}
}class Car {private Engine engine;public Car() { // 组合关系,Car 自己创建 Enginethis.engine = new Engine();}public void start() {engine.start();System.out.println("Car starts.");}
}public class CompositionExample {public static void main(String[] args) {Car car = new Car(); // Car 创建并拥有 Enginecar.start();}
}

1.3、聚合

  聚合是一种弱“拥有”关系,表示一个类包含另一个类的对象作为其属性,但被包含对象可以独立存在。整体并不控制部分的生命周期。

class Engine {public void start() {System.out.println("Engine starts.");}
}class Car {private Engine engine;public Car(Engine engine) { // 通过构造方法传递 Enginethis.engine = engine;}public void start() {engine.start();System.out.println("Car starts.");}
}public class AggregationExample {public static void main(String[] args) {Engine engine = new Engine(); // Engine 可以独立存在Car car = new Car(engine);   // Car 聚合了 Enginecar.start();}
}

1.4、依赖

  依赖是一种非常松散的关系,表示一个类使用另一个类的功能,但它们的生命周期彼此独立。

class Printer {public void printDocument(String document) {System.out.println("Printing: " + document);}
}class User {public void usePrinter(Printer printer, String document) {printer.printDocument(document);}
}public class DependencyExample {public static void main(String[] args) {Printer printer = new Printer(); // 创建 Printer 对象User user = new User();         // 创建 User 对象user.usePrinter(printer, "My Document"); // User 依赖于 Printer 的功能}
}

二、单一职责原则

  单一职责原则的核心思想在于,一个类,或下推到一个方法,只去做一件事,或者一类逻辑相同的事情。注意,此一件事,并不代表一个类中只能存在一个方法。例如我们有一个OrderService,顾名思义,应该在该类中做和订单相关的操作,比如下单,修改订单状态,撤单等。而不是再混入一些用户相关操作。

2.1、单一职责原则反面案例

/*** 单一职责原则案例1*/
public class Demo1 {public static void main(String[] args) {Vehicle vehicle = new Vehicle();vehicle.run("汽车");vehicle.run("船");vehicle.run("飞机");}
}/*** 违反单一职责原则,不同类的交通工具都打出了在公路上行驶* 实际上各自的行驶方式是不一样的* 这种写法把所有的情况都笼统地放在了一个方法中*/
class Vehicle{public void run(String vehicle){System.out.println(vehicle + "在公路上行驶");}
}

2.2、单一职责原则反面案例的改进

/*** 单一职责原则案例2*  这种方式是在类的层面上进行了拆分*  将不同的交通工具的行驶方式,拆分到了不同的类中*  在结构简单,每个类中代码量少的时候,没必要*/
public class Demo2 {public static void main(String[] args) {RoadVehicle roadVehicle = new RoadVehicle();roadVehicle.run("汽车");AirVehicle airVehicle = new AirVehicle();airVehicle.run("飞机");WaterVehicle waterVehicle = new WaterVehicle();waterVehicle.run("船");}
}class RoadVehicle{public void run(String vehicle){System.out.println(vehicle + "在公路上行驶");}
}class AirVehicle{public void run(String vehicle){System.out.println(vehicle + "在天空中飞行");}
}class WaterVehicle{public void run(String vehicle){System.out.println(vehicle + "在水上航行");}
}
/*** 单一职责原则案例3**/
public class Demo3 {public static void main(String[] args) {Vehicle2 vehicle2 = new Vehicle2();vehicle2.airRun("飞机");vehicle2.roadRun("汽车");vehicle2.waterRun("船");}
}/*** 这种方式是在方法层面进行了拆分,每个方法只做一件事*/
class Vehicle2{public void roadRun(String vehicle){System.out.println(vehicle + "在公路上行驶");}public void airRun(String vehicle){System.out.println(vehicle + "在天空中飞行");}public void waterRun(String vehicle){System.out.println(vehicle + "在水上航行");}
}

三、接口隔离原则

  接口隔离原则的核心思想在于,客户端不应该依赖于它不需要的接口。简单来说,接口隔离原则建议将大的、庞杂的接口拆分成多个小的、专注的接口,以减少类或模块对不必要方法的依赖。
  当一个接口包含了多个方法时,可能会导致一些实现该接口的类,不得不实现它们不需要的方法。这会增加代码的复杂性,造成不必要的耦合,从而降低系统的灵活性和可维护性。

3.1、接口隔离原则反面案例

/*** 接口隔离原则* 现在有i1一个接口。它有B,C两个实现类* B和D实现了接口的所有方法* A通过接口使用B 但是A只会用到接口中的123方法* C通过接口使用D 但是C只会用到接口的145方法* 虽然A,C只会用到接口中的部分方法,但是B和D因为implement关键字的特性,必须被迫实现接口的所有方法*/
public class Demo1 {public static void main(String[] args) {A a = new A();a.depend1(new B());a.depend2(new B());a.depend3(new B());}
}interface I1{void method1();void method2();void method3();void method4();void method5();
}class B implements I1{@Overridepublic void method1() {System.out.println("B实现了I1的method1");}@Overridepublic void method2() {System.out.println("B实现了I1的method2");}@Overridepublic void method3() {System.out.println("B实现了I1的method3");}@Overridepublic void method4() {System.out.println("B实现了I1的method4");}@Overridepublic void method5() {System.out.println("B实现了I1的method5");}
}class D implements I1{@Overridepublic void method1() {System.out.println("D实现了I1的method1");}@Overridepublic void method2() {System.out.println("D实现了I1的method2");}@Overridepublic void method3() {System.out.println("D实现了I1的method3");}@Overridepublic void method4() {System.out.println("D实现了I1的method4");}@Overridepublic void method5() {System.out.println("D实现了I1的method5");}
}class A{public void depend1(I1 i1){i1.method1();}public void depend2(I1 i1){i1.method2();}public void depend3(I1 i1){i1.method3();}
}class C{public void depend1(I1 i1){i1.method1();}public void depend4(I1 i1){i1.method4();}public void depend5(I1 i1){i1.method5();}
}

3.2、接口隔离原则反面案例的改进


/***A通过接口使用B 但是A只会用到接口中的123方法 * C通过接口使用D 但是C只会用到接口的145方法* 就拆出三个接口,接口一只有1方法让B,D实现,接口二有2,3方法,让B实现,接口三有4,5方法,让D实现。* 即B和D无需实现多余的方法
**/
public class Demo2 {public static void main(String[] args) {A1 a1 = new A1();a1.depend1(new B1());
//        a1.depend1(new D1());a1.depend3(new B1());a1.depend2(new B1());C1 c1 = new C1();c1.depend1(new D1());c1.depend4(new D1());c1.depend4(new D1());}
}interface Inter1{void method1();
}interface Inter2{void method2();void method3();
}interface Inter3{void method4();void method5();
}class B1 implements Inter1,Inter2{@Overridepublic void method1() {System.out.println("B实现了Inter1的method1");}@Overridepublic void method2() {System.out.println("B实现了Inter2的method2");}@Overridepublic void method3() {System.out.println("B实现了Inter2的method3");}
}class D1 implements Inter1,Inter3{@Overridepublic void method1() {System.out.println("D实现了Inter1的method1");}@Overridepublic void method4() {System.out.println("D实现了Inter3的method4");}@Overridepublic void method5() {System.out.println("D实现了Inter3的method5");}
}class A1{public void depend1(Inter1 inter1){inter1.method1();}public void depend2(Inter2 inter2){inter2.method2();}public void depend3(Inter2 inter2){inter2.method3();}
}class C1{public void depend1(Inter1 inter1){inter1.method1();}public void depend4(Inter3 inter3){inter3.method4();}public void depend5(Inter3 inter3){inter3.method5();}}

四、依赖倒转原则

  依赖倒转原则的核心思想在于面向接口编程,即高层模块不应该依赖低层模块。两者都应该依赖抽象。 抽象不应该依赖于细节。细节应该依赖于抽象。即高层模块和低层模块之间的依赖关系应该通过抽象(接口或抽象类)来连接。
  这里不得不提到抽象类接口的作用,简单地说,两者皆是一种规范,作为模版,屏蔽了实现的细节。具体的实现都是交给子类去完成。

4.1、依赖倒转原则反面案例

  在下面的案例中,发送邮件是一个具体的实现,用户在接受时也是接受了一个具体的实现。那么如果我现在不是发送邮件,而是需要发送短信或者微信呢?
  那么就需要改动接收方的代码,将传入的Email改成Message或者WeChat等。

public class Demo1 {public static void main(String[] args) {new Person().receive(new Email());}
}class Email{public String sendEmail(){return "发送邮件";}
}class Person{public void receive(Email email){System.out.println(email.sendEmail());}
}

4.1、依赖倒转原则反面案例的改进

  在改进的代码中,选择将发送微信,短信,或者邮件的动作,抽象成一个Message接口,在接受方法中,只需要传入Message接口,调用接受方法时,传入该接口对应的实现即可,也是面向对象多态特性的体现:

/*** 依赖倒转原则案例* 面向接口编程 只需要传递对应的实现类即可*/
public class Demo2 {public static void main(String[] args) {new Person().receive(new Email());new Person().receive(new Wechat());}
}interface Message{String sendMessage();
}class Email implements Message{@Overridepublic String sendMessage() {return "发送邮件";}
}class Wechat implements Message{@Overridepublic String sendMessage() {return "发送微信";}
}class Person{public void receive(Message message){System.out.println(message.sendMessage());}
}

五、里氏替换原则

  里氏替换原则的核心思想在于,要求子类能够完全替代父类使用,且不改变原有程序的正确性和行为。这意味着子类必须遵循父类定义的契约,确保在继承层次结构中,任何父类对象可以被子类对象所替换,并且不会引发错误或改变系统行为。并且,子类应该在不破坏父类已有行为的基础上,提供更多的功能,而不是改变父类的功能。
  如果要满足该原则,就需要尽量不重写父类中的方法。从某种程度来说,是否失去了继承的意义?其实不然,里氏替换原则只是告诉我们,不能滥用继承。里氏替换原则的核心目的是避免“强加不合理的行为”。即当你设计类层次结构时,应该确保父类的行为和子类的行为在语义上是一致的。就如最开始的案例一样,人可以继承人类去吃米饭,而猫不能继承人类去吃米饭,而应该吃猫粮。

5.1、里氏替换原则反面案例

/*** 里氏替换原则案例*/
public class Demo1 {public static void main(String[] args) {}
}class A{public int func1(int num1,int num2){return num1 - num2;}
}/*** B继承了A。无意中重写了func1方法,导致达不到预期的效果*/
class B extends A{@Overridepublic int func1(int num1, int num2) {return num1 + num2;}public int func2(int num1,int num2){return func1(num1,num2)+9;}
}

5.2、里氏替换原则反面案例的改进

/*** 里氏替换原则案例*/
public class Demo2 {public static void main(String[] args) {}
}/*** 将func1抽取到一个公共的接口中,哪个类需要用到func1,就自己实现接口去写自己的逻辑*/
interface Base{int func1(int num1,int num2);
}class A implements Base{@Overridepublic int func1(int num1,int num2){return num1 - num2;}
}/*** 如果B一定要用到A中的func1的逻辑,就使用组合的方式*/
class B implements Base{private A a = new A();@Overridepublic int func1(int num1, int num2) {return num1 + num2;}public int func2(int num1,int num2){return a.func1(num1,num2)+9;}
}

六、开闭原则

  开闭原则的核心思想在于,面对使用方的修改关闭,面对提供方的扩展开放,即:需求变化或新需求出现时,我们应该能够通过增加新的代码(如新的类或方法)来扩展系统的功能。现有的代码不应被修改,特别是已经经过测试并投入使用的部分。通过扩展而不是修改,避免破坏现有的功能,以及增加回归测试的成本
  这一点在实际工作中深有体会,如果因为修改而破坏了原有的功能,以及修改一处导致其他位置出现问题,牵一发而动全身,只能是说明最初的设计存在问题,没有考虑完全。

6.1、开闭原则反面案例

/*** 测试开闭原则*/
public class Demo1 {public static void main(String[] args) {}
}/*** 如果这个时候又要加一个画其他图形的需求呢?* 那么需要改动if条件判断,还要加一个新方法*/
class GraphicEditor{public void drawShape(Shape s){if (s.type == 1){this.drawRectangle();}else if (s.type == 2){this.drawCircle();}}public void drawRectangle(){System.out.println("画矩形");}public void drawCircle(){System.out.println("画圆");}}class Shape{int type;
}class Rectangle extends Shape{public Rectangle(){super.type = 1;}
}class Circle extends Shape{public Circle(){super.type = 2;}
}

6.2、开闭原则反面案例的改进

  实际上也是依赖倒转原则的体现,利用接口或者抽象类作为中间层解耦。

/*** 测试开闭原则* 改进*/
public class Demo2 {public static void main(String[] args) {new Rectangle().draw();}
}/*** 将画图改造成抽象类,要画什么图形,就自己继承抽象类加上自己的逻辑即可*/
abstract class GraphicEditor{void draw(){}
}class Rectangle extends GraphicEditor{@Overridevoid draw() {System.out.println("画矩形");}
}class Circle extends GraphicEditor{@Overridevoid draw() {System.out.println("画圆形");}
}class Triangle extends GraphicEditor{@Overridevoid draw() {System.out.println("画三角形");}
}

七、迪米特原则

  迪米特原则的核心思想在于,和本类功能相关的代码,就应该尽量放在本类的相关方法中,而不是放在方法的调用方。即调用方不需要关心被调用方法的具体实现,这也是面向对象和面向过程的区别。即对象应该尽可能地少知道其他对象的内部实现或细节。
  并且对象之间的交互应该尽量局限在直接的合作伙伴(即直接的成员变量、方法参数或者构造函数注入的对象)之间,而避免调用链过长的对象。

7.1、迪米特原则反面案例

/*** 演示迪米特法则*/
public class Demo1 {public static void main(String[] args) {SchoolManager schoolManager = new SchoolManager();schoolManager.printAll(new CollageManager());}
}class Employee{private String id;/*** 获取* @return id*/public String getId() {return id;}/*** 设置* @param id*/public void setId(String id) {this.id = id;}}class CollageEmployee{private String id;/*** 获取* @return id*/public String getId() {return id;}/*** 设置* @param id*/public void setId(String id) {this.id = id;}
}class CollageManager{public List<CollageEmployee> getAllCollageEmployee(){ArrayList<CollageEmployee> employeeEmployees = new ArrayList<>();for (int i = 0; i < 10; i++) {CollageEmployee employeeEmployee = new CollageEmployee();employeeEmployee.setId("学院员工id= "+i);employeeEmployees.add(employeeEmployee);}return employeeEmployees;}
}class SchoolManager{//CollageEmployee违反了迪米特原则,在printAll中,不关心CollageManager 是怎么把自己员工的信息打印出来的//应该把打印分公司员工的 信息 放到CollageManager类中public List<Employee> getAllEmployee(){ArrayList<Employee> employeeEmployees = new ArrayList<>();for (int i = 0; i < 5 ; i++) {Employee employeeEmployee = new Employee();employeeEmployee.setId("学校总部员工= "+i);employeeEmployees.add(employeeEmployee);}return employeeEmployees;}public void printAll(CollageManager collageManager){List<CollageEmployee> allCollageEmployee = collageManager.getAllCollageEmployee();System.out.println("分公司员工-------------");for (CollageEmployee c : allCollageEmployee) {System.out.println(c.getId());}List<Employee> allEmployee = this.getAllEmployee();System.out.println("总公司员工-------------");for (Employee employee : allEmployee) {System.out.println(employee.getId());}}
}

7.2、迪米特原则反面案例的改进

package com.light.principle.demeter.demo2;import java.util.ArrayList;
import java.util.List;/*** 演示迪米特法则*/
public class Demo2 {public static void main(String[] args) {SchoolManager schoolManager = new SchoolManager();schoolManager.printAll(new CollageManager());}
}class Employee{private String id;/*** 获取* @return id*/public String getId() {return id;}/*** 设置* @param id*/public void setId(String id) {this.id = id;}}class CollageEmployee{private String id;/*** 获取* @return id*/public String getId() {return id;}/*** 设置* @param id*/public void setId(String id) {this.id = id;}
}class CollageManager{public List<CollageEmployee> getAllCollageEmployee(){ArrayList<CollageEmployee> employeeEmployees = new ArrayList<>();for (int i = 0; i < 10; i++) {CollageEmployee employeeEmployee = new CollageEmployee();employeeEmployee.setId("学院员工id= "+i);employeeEmployees.add(employeeEmployee);}return employeeEmployees;}//把打印员工信息放回到CollageManager类中public void printCollageEmployee(){List<CollageEmployee> allCollageEmployee = this.getAllCollageEmployee();System.out.println("分公司员工-------------");for (CollageEmployee c : allCollageEmployee) {System.out.println(c.getId());}}
}class SchoolManager{public List<Employee> getAllEmployee(){ArrayList<Employee> employeeEmployees = new ArrayList<>();for (int i = 0; i < 5 ; i++) {Employee employeeEmployee = new Employee();employeeEmployee.setId("学校总部员工= "+i);employeeEmployees.add(employeeEmployee);}return employeeEmployees;}public void printAll(CollageManager collageManager){collageManager.printCollageEmployee();List<Employee> allEmployee = this.getAllEmployee();System.out.println("总公司员工-------------");for (Employee employee : allEmployee) {System.out.println(employee.getId());}}
}

八、合成复用原则

  合成复用原则实际上是对里氏替换原则的一个总结,它的核心思想是:通过对象组合(Composition)而不是继承(Inheritance)来实现复用。在设计系统时,我们应该优先考虑将不同的功能或者行为封装成独立的对象,然后通过组合这些对象来实现所需的功能,而不是通过继承关系来复用代码。

8.1、合成复用原则反面案例

class Bird {public void fly() {System.out.println("Flying");}public void eat() {System.out.println("Eating");}
}class Sparrow extends Bird {public void chirp() {System.out.println("Chirping");}
}class Penguin extends Bird {public void swim() {System.out.println("Swimming");}// 企鹅不应该飞行,但继承了 Bird,可能会误用 fly()@Overridepublic void fly() {throw new UnsupportedOperationException("Penguins can't fly");}
}

8.2、合成复用原则反面案例的改进

  在改进案例中,将飞行和游泳拆分为了两个独立的接口,细化了各自的功能,实现类可以按需选择。

// 定义飞行行为接口
interface Flyable {void fly();
}// 定义游泳行为接口
interface Swimmable {void swim();
}// Bird 类只负责鸟类的通用行为
class Bird {public void eat() {System.out.println("Eating");}
}// 麻雀类通过实现 Flyable 实现飞行
class Sparrow extends Bird implements Flyable {public void fly() {System.out.println("Sparrow flying");}public void chirp() {System.out.println("Chirping");}
}// 企鹅类通过实现 Swimmable 实现游泳
class Penguin extends Bird implements Swimmable {public void swim() {System.out.println("Penguin swimming");}
}

从下一篇开始,对23种设计模式进行逐条解析。

相关文章:

设计模式の软件设计原则

文章目录 前言一、聚合&组合&继承&依赖1.1、继承1.2、组合1.3、聚合1.4、依赖 二、单一职责原则2.1、单一职责原则反面案例2.2、单一职责原则反面案例的改进 三、接口隔离原则3.1、接口隔离原则反面案例3.2、接口隔离原则反面案例的改进 四、依赖倒转原则4.1、依赖…...

Linux centos7 下载MySQL5.7仓库的命令

wget 是一个非常强大的命令行工具&#xff0c;用于从网络上下载文件。它是 Linux 和其他 Unix-like 系统中常用的工具之一。wget 命令的各个参数有着不同的含义&#xff0c;下面是您提供的命令 wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.onarch.r…...

CSS flex布局踩坑小记:flex-basis属性之0px与0%的差异 (赞)

原文出处&#xff1a;CSS flex布局踩坑小记&#xff1a;flex-basis属性之0px与0%的差异_flex-basis 0%-CSDN博客 讲述flex容器被撑大的原因(误用&#xff1a;flex-basis: 0%;)及解决方法(用&#xff1a;flex-basis: 0px;)...

华硕主板不能开启

正常流程&#xff1a; [主機板]BIOS如何設置主機板整合圖形(內顯)和獨立顯示卡同時顯示輸出 | 官方支援 | ASUS 台灣 如果开启了CSR兼容性模式&#xff0c;在BIOS里面&#xff0c;就必须关掉&#xff0c;才能支持多显示器&#xff0c;如下图显示的标识才会出现。...

室联人形机器人:家政服务任务结构化、技术要点、深入应用FPGA的控制系统框架设计(整合版)

目录&#xff1a; 0 引言 1 人形机器人对室内家政服务任务的结构化 1.1人形机器人在室内家政服务中的比较优势 1.1.1 人形机器人拟人性的7个维度 1.1.2 拟人性在室内家政服务工作中的比较优势 1.1.3 潜在的重要用户&#xff1a;宠物爱好者 1.2 居所室内环境的特征与结构…...

OpenAI 发布 o1 LLM,推出 ChatGPT Pro

OpenAI正式发布了专为复杂推理而构建的 OpenAI o1大型语言模型(LLM)。 该公司还推出了 ChatGPT Pro&#xff0c;这是一项每月 200 美元的套餐&#xff0c;包括无限制访问 OpenAI o1、o1-mini、GPT-4o 和高级语音对话。 OpenAI o1 从 9 月 12 日起在 ChatGPT 中推出预览版&…...

【MySQL】存储过程和触发器

MySQL存储过程和触发器 一、存储过程的介绍二、存储过程的相关操作2.1创建存储过程2.2查看存储过程2.4调用存储过程2.5删除存储过程 三、变量3.1系统变量3.2用户定义变量3.3局部变量 四、存储过程中的关键字4.1 if4.2参数4.3case4.4 while4.5repeat4.6 loop4.7游标4.8条件处理程…...

QT4和 QT5 槽函数连接的区别

正常连接方式 //QT4官方用列QLabel *label new QLabel;QScrollBar *scrollBar new QScrollBar;QObject::connect(scrollBar, SIGNAL(valueChanged(int)),label, SLOT(setNum(int)));//QT5官方用列QLabel *label new QLabel;QLineEdit *lineEdit new QLineEdit;QObject::c…...

使用 PyTorch 和 Horovod 来编写一个简单的分布式训练 demo

使用 PyTorch 和 Horovod 来编写一个简单的分布式训练 demo&#xff0c;可以帮助你理解如何在多GPU或多节点环境中高效地训练深度学习模型。Horovod 是 Uber 开发的一个用于分布式训练的框架&#xff0c;它支持 TensorFlow、Keras、PyTorch 等多个机器学习库。下面是一个基于 P…...

SQL复杂查询功能介绍及示例

文章目录 1. 多表连接&#xff08;JOIN&#xff09;功能介绍应用场景示例查询及初始表格customers 表&#xff08;未查询前&#xff09;orders 表&#xff08;未查询前&#xff09;INNER JOIN 示例LEFT JOIN 示例 2. 子查询&#xff08;Subquery&#xff09;功能介绍应用场景示…...

shell基础用法

shell基础知识 shell中的多行注释 :<<EOF read echo $REPLY # read不指定变量&#xff0c;则默认写入$REPLY EOF # :<<EOF ...EOF 多行注释&#xff0c;EOF可以替换为&#xff01;# 等文件目录和执行目录 echo $0$0 # ./demo.sh echo $0的realpath$(realpath…...

C#设计模式--策略模式(Strategy Pattern)

策略模式是一种行为设计模式&#xff0c;它使你能在运行时改变对象的行为。在策略模式定义了一系列算法或策略&#xff0c;并将每个算法封装在独立的类中&#xff0c;使得它们可以互相替换。通过使用策略模式&#xff0c;可以在运行时根据需要选择不同的算法&#xff0c;而不需…...

【opencv入门教程】15. 访问像素的十四种方式

文章选自&#xff1a; 一、像素访问 一张图片由许多个点组成&#xff0c;每个点就是一个像素&#xff0c;每个像素包含不同的值&#xff0c;对图像像素操作是图像处理过程中常使用的 二、访问像素 void Samples::AccessPixels1(Mat &image, int div 64) {int nl imag…...

【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)

导航&#xff1a; 本文一些内容需要聚簇索引、非聚簇索引、B树、覆盖索引、索引下推等前置概念&#xff0c;虽然本文有简单回顾&#xff0c;但详细可以参考下文的【MySQL高级篇】 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/谷粒商城/学成在线设计模…...

根据html的段落长度设置QtextBrowser的显示内容,最少显示一个段落

要根据 HTML 段落的长度设置 QTextBrowser 的显示内容&#xff0c;并确保至少显示一个段落&#xff0c;可以通过以下步骤来实现&#xff1a; 加载 HTML 内容&#xff1a;首先&#xff0c;你需要加载 HTML 内容到 QTextBrowser 中。可以通过 setHtml() 方法来设置 HTML。 计算段…...

基于Huffman编码的GPS定位数据无损压缩算法

目录 一、引言 二、霍夫曼编码 三、经典Huffman编码 四、适应性Huffman编码 五、GPS定位数据压缩 提示&#xff1a;文末附定位数据压缩工具和源码 一、引言 车载监控系统中&#xff0c;车载终端需要获取GPS信号&#xff08;经度、纬 度、速度、方向等&#xff09;实时上传…...

php:完整部署Grid++Report到php项目,并实现模板打印

一、下载Grid++Report软件 路径:开发者安装包下载 - 锐浪报表工具 二、 安装软件 1、对下载的压缩包运行内部的exe文件 2、选择语言 3、 完成安装引导 下一步即可 4、接收许可协议 点击“我接受” 5、选择安装路径 “浏览”选择安装路径,点击"安装" 6、完成…...

C标签和 EL表达式的在前端界面的应用

目录 前言 常用的c标签有&#xff1a; for循环 1 表示 普通的for循环的 2 常在集合中使用 表示 选择关系 1 简单的表示如果 2 表示如果。。否则。。 EL表达式 格式 &#xff1a; ${属性名/对象/ 集合} 前言 本篇博客介绍 c标签和el表达式的使用 使用C标签 要引入 …...

Linux絮絮叨(四) 系统目录结构

Linux 系统的目录结构&#xff08;Filesystem Hierarchy Standard, FHS&#xff09;定义了 Linux 系统中文件系统的标准布局&#xff0c;以下是一些常见目录的功能&#xff1a; 根目录 / 描述&#xff1a;所有文件和目录的起始点&#xff0c;Linux 文件系统的根。内容&#xf…...

Java基于SpringBoot的网上订餐系统,附源码

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…...

《Java核心技术I》死锁

死锁 账户1&#xff1a;200元账户2: 300元线程1&#xff1a;从账号1转300到账户2线程2&#xff1a;从账户2转400到账户1 如上&#xff0c;线程1和线程2显然都被阻塞&#xff0c;两个账户的余额都不足以转账&#xff0c;两个线程都无法执行下去。 有可能会因为每一个线程要等…...

【Windows11系统局域网共享文件数据】

【Windows11系统局域网共享文件数据】 1. 引言1. 规划网络2. 获取必要的硬件3. 设置网络4. 配置网络设备5. 测试网络连接6. 安全性和维护7. 扩展和优化 2. 准备工作2.1: 启用网络发现和文件共享2.2: 设置共享文件夹 3. 访问共享文件夹4. 小贴士5. 总结 1. 引言 随着家庭和小型办…...

MCU、ARM体系结构,单片机基础,单片机操作

计算机基础 计算机的组成 输入设备、输出设备、存储器、运算器、控制器 输入设备&#xff1a;将其他信号转换为计算机可以识别的信号&#xff08;电信号&#xff09;。输出设备&#xff1a;将电信号&#xff08;&#xff10;、&#xff11;&#xff09;转为人或其他设备能理解的…...

在办公室环境中用HMD替代传统显示器的优势

VR头戴式显示器&#xff08;HMD&#xff09;是进入虚拟现实环境的一把钥匙&#xff0c;拥有HMD的您将能够在虚拟现实世界中尽情探索未知领域&#xff0c;正如如今的互联网一样&#xff0c;虚拟现实环境能够为您提供现实中无法实现的或不可能实现的事。随着技术的不断进步&#…...

ssm 多数据源 注解版本

application.xml 配置如下 <!-- 使用 DruidDataSource 数据源 --><bean id"primaryDataSource" class"com.alibaba.druid.pool.DruidDataSource" init-method"init" destroy-method"close"></bean> <!-- 使用 数…...

selenium常见接口函数使用

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;测试_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 1. 查找 查找方式 css_s…...

STM32F103单片机使用STM32CubeMX新建IAR工程步骤

打开STM32CubeMX软件&#xff0c;选择File 选择新建工程 在打开的窗口输入单片机型号 在右下角选择单片机型号&#xff0c;然后点右上角 start project&#xff0c;开始新建工程。 接下来设置调试接口&#xff0c;在左边System Core中选择 SYS&#xff0c;然后在右右边debu…...

刷题重开:找出字符串中第一个匹配项的下标——解题思路记录

问题描述&#xff1a; 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 示例 1&#xff1a; 输入&…...

product/admin/list?page=0size=10field=jancodevalue=4562249292272

文章目录 1、ProductController2、AdminCommonService3、ProductApiService4、ProductCommonService5、ProductSqlService https://api.crossbiog.com/product/admin/list?page0&size10&fieldjancode&value45622492922721、ProductController GetMapping("ad…...

人工智能机器学习无监督学习概念及应用详解

无监督学习&#xff1a;深入解析 引言 在人工智能和机器学习的领域中&#xff0c;无监督学习&#xff08;Unsupervised Learning&#xff09;是一种重要的学习范式。与监督学习不同&#xff0c;无监督学习不依赖于标签数据&#xff0c;而是通过模型从无标签的数据中学习数据的…...