设计模式的原则(一)

news2024/11/19 23:31:01

相信自己,无论自己到了什么局面,请一定要继续相信自己。

新的世界开始了,接下来,老蝴蝶带领大家学习一下设计模式。

我们先了解一下 设计原则

一.设计模式

一.一 设计原则

设计模式常用的七大原则:

  1. 单一职责原则
  2. 接口隔离原则
  3. 依赖倒转(倒置)原则
  4. 里氏替换原则
  5. 开闭原则
  6. 迪米特法则
  7. 合成复用原则

一.二 设计模式的目的

编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,可重用性,灵活性等多方面的挑战。
设计模式是为了让程序和软件具有更好的:

  1. 代码重用性 (即: 相同功能的代码,不用多次编写)
  2. 可读性 (即: 编程规范性,便于其他程序员的阅读和理解)
  3. 可扩展性 (即: 当需要增加新的功能时,非常的方便,也叫做 可维护性)
  4. 可靠性 (即: 当我们增加新的功能后,对原来的功能没有影响)
  5. 使程序呈现高内聚,低耦合的特征。

二. 单一职责原则

二.一 基本介绍

一个类,应该只负责一项职责。 如果类 Run 负责两个不同的职责, 如: 公路跑, 天上跑, 那么当职责 1 需求发生改变时,则需要修改 Run 类,
那么在修改的过程中,可能会造成职责 2 执行错误。 所以需要将 Run 类的粒度进行分解, 分解成 公路跑, 天上跑,水里游。

二.二 代码举例

二.二.一 错误的处理

@Slf4j
public class Run {
    public void run(String vehicle) {
        log.info(" {} 在公路上跑", vehicle);
    }
}

测试方法:

  @Test
    public void testA() {
        Run run = new Run();
        run.run("小汽车");
        run.run("飞机");
        run.run("鱼");
    }

image-20230612203716009

这是不对的.

二.二.二 单一职责

空中飞的一个类:

@Slf4j
public class AirRun {
    public void run(String vehicle) {
        log.info(" {} 在空中飞", vehicle);
    }
}

路上跑的一个类:

@Slf4j
public class LuRun {
    public void run(String vehicle) {
        log.info(" {} 在路上跑", vehicle);
    }
}

水里游的一个类:

@Slf4j
public class WaterRun {
    public void run(String vehicle) {
        log.info(" {} 在水中游", vehicle);
    }
}

测试方法:

 @Test
    public void testB() {
        LuRun luRun = new LuRun();
        luRun.run("小汽车");
        AirRun airRun = new AirRun();
        airRun.run("飞机");
        WaterRun waterRun = new WaterRun();
        waterRun.run("鱼");
    }

image-20230612203853805

二.二.三 方法单一职责

方法单一:

@Slf4j
public class RunTotal {
    public void luRun(String vehicle) {
        log.info(" {} 在公路上跑", vehicle);
    }

    public void airRun(String vehicle) {
        log.info(" {} 在空中飞", vehicle);
    }

    public void waterRun(String vehicle) {
        log.info(" {} 在水中游", vehicle);
    }
}

测试方法:

 @Test
    public void testC() {
        RunTotal runTotal = new RunTotal();
        runTotal.luRun("小汽车");
        runTotal.airRun("飞机");
        runTotal.waterRun("鱼");
    }

image-20230612203942551

testA() 不同的交通工具在同一个方法中执行。

testB(), 遵守单一职责, 每一个类进行不同的处理。

testC() 类中每一个方法进行单一职责

二.三 注意事项

  1. 降低类的复杂度, 一个类只负责一项职责
  2. 提高类的可读性,可维护性
  3. 降低变更引起的风险
  4. 通常情况下,应该遵守单一职责原则, 当类中方法数量足够少时,可以在方法级别保持单一职责原则。

三. 接口隔离原则

三.一 基本介绍

一个类对另一个类的依赖,应该建立在最小的接口上.

三.二 代码举例

有五个操作 operation1 operation2 operation3 operation4 operation5

其中, A类 会使用到 1 2,3 接口。 C类会用到 1,4,5 接口。

B 会依赖 A, C 也依赖 D

三.二.一 错误的代码

定义一个接口,有5个接口

public interface InterfaceFive {
     void operation1();
    void operation2();
    void operation3();
    void operation4();
    void operation5();
}

A类:

public class ClassA {
    public void depend1(InterfaceFive interfaceFive) {
        interfaceFive.operation1();
    }
    public void depend2(InterfaceFive interfaceFive) {
        interfaceFive.operation2();
    }
    public void depend3(InterfaceFive interfaceFive) {
        interfaceFive.operation3();
    }
}

B 类:

@Slf4j
public class ClassB implements InterfaceFive{

    @Override
    public void operation1() {
        log.info("B实现操作1");
    }

    @Override
    public void operation2() {
        log.info("B实现操作2");
    }

    @Override
    public void operation3() {
        log.info("B实现操作3");
    }

    @Override
    public void operation4() {
        log.info("B实现操作4");
    }

    @Override
    public void operation5() {
        log.info("B实现操作5");
    }
}

C类:

public class ClassC {
    public void depend1(InterfaceFive interfaceFive) {
        interfaceFive.operation1();
    }
    public void depend4(InterfaceFive interfaceFive) {
        interfaceFive.operation4();
    }
    public void depend5(InterfaceFive interfaceFive) {
        interfaceFive.operation5();
    }
}

D类:

@Slf4j
public class ClassD implements InterfaceFive{

    @Override
    public void operation1() {
        log.info("D实现操作1");
    }

    @Override
    public void operation2() {
        log.info("D实现操作2");
    }

    @Override
    public void operation3() {
        log.info("D实现操作3");
    }

    @Override
    public void operation4() {
        log.info("D实现操作4");
    }

    @Override
    public void operation5() {
        log.info("D实现操作5");
    }
}

方法测试:

 @Test
    public void testOne() {
        // 对 A 处理
        ClassA classA = new ClassA();
        classA.depend1(new ClassB());
        classA.depend2(new ClassB());
        classA.depend3(new ClassB());

        // 对 C 处理
        ClassC classC = new ClassC();
        classC.depend1(new ClassD());
        classC.depend4(new ClassD());
        classC.depend5(new ClassD());
    }

image-20230612204344102

三.二.二 接口隔离

将接口 InterfaceFive 拆分为独立的几个接口(这里我们拆分成 3 个接口),类A和类 C 分别与他们需要的接口建立 依赖关系。也就是采用接口隔离原则

接口1

public interface Interface1 {
    void operation1();
}

接口2:

public interface Interface2 {
    void operation2();
    void operation3();
}

接口3:

public interface Interface3 {
    void operation4();
    void operation5();
}

类A:

public class NewClassA {
    public void depend1(Interface1 interface1) {
        interface1.operation1();
    }
    public void depend2(Interface2 interface2) {
        interface2.operation2();
    }
    public void depend3(Interface2 interface2) {
        interface2.operation3();
    }
}

类B:

@Slf4j
public class NewClassB implements Interface1,Interface2 {

    @Override
    public void operation1() {
        log.info("B实现操作1");
    }

    @Override
    public void operation2() {
        log.info("B实现操作2");
    }

    @Override
    public void operation3() {
        log.info("B实现操作3");
    }

}

类C:

public class NewClassC {
    public void depend1(Interface1 interface1) {
        interface1.operation1();
    }
    public void depend4(Interface3 interface3) {
        interface3.operation4();
    }
    public void depend5(Interface3 interface3) {
        interface3.operation5();
    }
}

类D:

@Slf4j
public class NewClassD implements Interface1,Interface3 {

    @Override
    public void operation1() {
        log.info("D实现操作1");
    }

    @Override
    public void operation4() {
        log.info("D实现操作4");
    }

    @Override
    public void operation5() {
        log.info("D实现操作5");
    }
}

方法验证:

/**
     将接口 拆分成   1   23   45
     分别去依赖使用,  这样 A 不会拥有 4,5 功能 , C 不会拥有 1,2功能。
     即将 接口的能力进行了约束
     */
    @Test
    public void testTwo() {
        NewClassA classA = new NewClassA();
        classA.depend1(new NewClassB());
        classA.depend2(new NewClassB());
        classA.depend3(new NewClassB());

        // 对 C 处理
        NewClassC classC = new NewClassC();
        classC.depend1(new NewClassD());
        classC.depend4(new NewClassD());
        classC.depend5(new NewClassD());
    }

image-20230612204546630

三.三 注意事项

接口拆分的粗细度

四. 依赖倒转原则

四.一 基本介绍

  1. 高层模块不应该依赖低层模块,二者都应该依赖其抽象
  2. 抽象不应该依赖细节, 细节应该依赖抽象
  3. 依赖倒转的中心思想是 面向接口编程
  4. 依赖倒转原则是基于这样的设计理念: 相对于细节的多变性,抽象的东西要稳定的多。
    以抽象为基础搭建的架构比以细节为基础搭建的架构要稳定的多。 在 Java中 抽象指的是
    接口或者抽象类,细节指的是具体的实现类
  5. 使用接口或者抽象类的目的是制定好规范,而不涉及任何具体的操作,把展示细节的任务交给他们的实现类去完成。

四.二 代码实例

如实例:

定义一个通知用户的操作

四.二.一 错误实例

邮箱通知:

public class Email {

    public String getInfo() {
        return "构建邮件信息,并进行发送";
    }
}

微信通知:

public class WeiXin {
    public String getInfo() {
        return "构建微信信息,并进行发送";
    }
}

消息发送:

@Slf4j
public class Message {
    public void send(Email email) {
        String info = email.getInfo();
        log.info(">>> 获取邮件信息,并进行发送 {}", info);
    }

    public void send(WeiXin weiXin) {
        String info = weiXin.getInfo();
        log.info(">>> 获取微信信息,并进行发送 {}", info);
    }
}

代码测试:

 @Test
    public void sendOneTest() {
        Message message = new Message();
        message.send(new Email());
        message.send(new WeiXin());
    }

image-20230612204910347

四.二.二 依赖转置

定义一个接口, 然后 微信 和邮箱进行实现

public interface SendMessage {

    // 实际是一个对象
    public String getInfo();
}

邮箱:

public class NewEmail implements SendMessage{

    @Override
    public String getInfo() {
        return "构建邮件信息,并进行发送";
    }
}

微信:

public class NewWeiXin implements SendMessage{

    @Override
    public String getInfo() {
        return "构建微信信息,并进行发送";
    }
}

发送:

@Slf4j
public class NewMessage {
    public void send(SendMessage sendMessage) {
        String info = sendMessage.getInfo();
        log.info(">>> 发送 {}", info);
    }
}

测试方法:

 /**
    将消息提取成一个接口,  然后邮箱 和微信进行实现。
     */
    @Test
    public void sendTwoTest() {
        NewMessage message = new NewMessage();
        message.send(new NewEmail());
        message.send(new NewWeiXin());
    }

image-20230612205048166

依赖关系传递的三种方案

即有 功能 A, 功能 B, 如何要想 A 使用 B, 则如何处理 ?

  1. 接口实现 A extends B , A implements B
  2. 构造方法传递 public A ( B b )
  3. setter 方法设置 A 中有属性 B , a.setB(b) 再使用

四.三 注意事项

  1. 低层模块尽量都要有抽象类或者接口, 或者两者都有,程序稳定性更好。
  2. 变量的声明类型尽量是抽象类或接口, 这样变量引用和实际对象之间,就存在一个缓冲区,利于程序的扩展和优化。
  3. 继承时遵循里氏替换原则

五. 里氏替换原则

五.一 基本介绍

继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,
虽然它不强制要求所有 的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏

继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,
增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且 父类修改后,所有涉及到子类的功能都有可能产生故障

里氏替换原则(Liskov Substitution Principle)在 1988 年,由麻省理工学院的以为姓里的女士提出的。

如果对每个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,
使得以 T1 定义的所有程序 P 在所有的对象 o1 都 代换成 o2 时,程序 P 的行为没有发生变化,
那么类型T2 是类型 T1 的子类型。换句话说,所有引用基类的地 方必须能透明地使用其子类的对象。

在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法

里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来 解决问题

五.二 代码实例

五.二.一 错误代码

基础操作A

public class OperationA {

    public int sub ( int a, int b) {
        return a - b;
    }
}

扩展操作B

public class OperationB extends OperationA{

    @Override
    public int sub(int a, int b) {
       // 对方法进行了改变, 无意重写。 如果不重写的话,当 operationA.sub 方法变化时,所有的子类都会变化。
        return a + b;
    }

    public int div( int a, int b) {
        return a /b;
    }
}

测试方法:

    @Test
    public void testOne() {
        OperationA operationA = new OperationA();
        log.info("3-1={}",operationA.sub(3,1));

        OperationB operationB = new OperationB();
        log.info("3-1={}",operationB.sub(3,1));
        log.info("3/1={}",operationB.div(3,1));
    }

image-20230612205412540

五.二.二 里氏替换

定义一个空的接口,表示能力

public interface BaseOperation {

}

原操作

public class NewOperationA implements BaseOperation{
    public int sub ( int a, int b) {
        return a - b;
    }
}

扩展操作:

public class NewOperationB implements BaseOperation{
    private NewOperationA operationA = new NewOperationA();

    public int oldSub(int a, int b) {
        // 对方法进行了改变, 无意重写。 如果不重写的话,当 operationA.sub 方法变化时,所有的子类都会变化。
        return operationA.sub(a,b);
    }

    public int sub(int a, int b) {
       // 对其重写的
        return a+b;
    }

    public int div( int a, int b) {
        return a /b;
    }
}

测试方法:

/**
     同时继承一个 BaseOperation 类, 表示接口的能力进行处理。
     */
    @Test
    public void testTwo() {
        NewOperationA operationA = new NewOperationA();
        log.info("3-1={}",operationA.sub(3,1));

        NewOperationB operationB = new NewOperationB();
        log.info("3+1={}",operationB.sub(3,1));
        log.info("3-1={}",operationB.oldSub(3,1));
        log.info("3/1={}",operationB.div(3,1));
    }

image-20230612205552094

五.三 注意事项

六. 开闭原则

六.一 基本介绍

开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则

  1. 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实 现扩展细节
  2. 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化
  3. 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则

六.二 代码实例

六.二.一 错误实例

画图形接口

public interface Shape {
    int getType();
}

矩形

public class Rectangle implements Shape{

    @Override
    public int getType() {
        return 0;
    }
}

圆形

public class Circle implements Shape{

    @Override
    public int getType() {
        return 1;
    }
}

画图形

@Slf4j
public class DrawShape {

    public void drawShape( Shape shape) {
        if (shape.getType() == 0) {
            drawRectange();
        }else if (shape.getType() == 1) {
            drawCircle();
        }
    }
	// 需要改变
    private void drawCircle() {
        log.info(">>> 画圆");
    }

    private void drawRectange() {
        log.info(">>> 画方形");
    }
}

测试方法:

   @Test
    public void oneTest() {
        DrawShape drawShape = new DrawShape();
        drawShape.drawShape(new Rectangle());
        drawShape.drawShape(new Circle());
    }

image-20230612205842663

六.二.二 开闭原则

接口:

public interface NewShape {
    int getType();

    void drawShape();
}

矩形:

@Slf4j
public class NewRectangle implements NewShape{

    @Override
    public int getType() {
        return 0;
    }

    @Override
    public void drawShape() {
        log.info(">>> 画方形");
    }
}

圆形

@Slf4j
public class NewCircle implements NewShape{

    @Override
    public int getType() {
        return 1;
    }

    @Override
    public void drawShape() {
        log.info(">>> 画圆形");
    }
}

画图形:

@Slf4j
public class NewDrawShape {

    public void drawShape( NewShape shape) {
        shape.drawShape();
    }
}

测试方法:

 @Test
    public void twoTest() {
        NewDrawShape drawShape = new NewDrawShape();
        drawShape.drawShape(new NewRectangle());
        drawShape.drawShape(new NewCircle());
    }

image-20230612210005742

六.三 注意事项

七. 迪米特法则

七.一 基本介绍

  1. 一个对象应该对其他对象保持最少的了解
  2. 类与类关系越密切,耦合度越大。
  3. 迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于 被依赖的类不管多么复杂,
    都尽量将逻辑封装在类的内部,对外除了提供的public 方法,不对外泄露任何信息。
  4. 迪米特法则还有个更简单的定义, 只与直接的朋友通信。
  5. 直接的朋友: 每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。
    耦合的方式有很多,依赖,关联,组合,聚合等。 其中我们称出现 成员变量,方法参数,方法返回值的类为直接的朋友,而出现在局部
    变量中的类不是直接的朋友。 也就是说,陌生的类最好不要以局部变量的形式出现在类的内部

七.二 代码实例

打印用户和部门信息

七.二.一 错误实例

部门:

@Data
@Builder
public class Dept {
    private Integer id;
    private String name;
}

部门查询列表:

public class DeptManager {
    public List<Dept> findAll() {
        List<Dept> result = new ArrayList<>();

        for (int i = 0;i< 5;i++) {
            result.add(Dept.builder().id(i+1).name("随机部门"+i).build());
        }
        return result;
    }
}

用户:

@Data
@Builder
public class User {
    private Integer id;
    private String name;
}

用户打印时,将部门也进行打印:

@Slf4j
public class UserManager {

    public List<User> findAll() {
        List<User> result = new ArrayList<>();

        for (int i = 0;i< 5;i++) {
            result.add(User.builder().id(i+1).name("随机名称"+i).build());
        }

        return result;
    }


    public void printAll (DeptManager deptManager) {
        // 发现 在这个类里面 有 Dept 类,但这个类并不是 形参,成员变量,方法返回值, 不算是直接朋友。
        List<Dept> deptAll = deptManager.findAll();
        for (Dept dept: deptAll) {
            log.info(">>> 部门 {}", dept);
        }

        List<User> userAll = findAll();
        for (User user: userAll) {
            log.info(">>> 员工 {}", user);
        }
    }
}

测试方法:

  @Test
    public void testOne() {
        UserManager userManager = new UserManager();
        userManager.printAll(new DeptManager());
    }

image-20230612210338898

七.二.二 迪米特法则

部门添加一个打印的方法

@Slf4j
public class NewDeptManager {
    public List<Dept> findAll() {
        List<Dept> result = new ArrayList<>();

        for (int i = 0;i< 5;i++) {
            result.add(Dept.builder().id(i+1).name("随机部门"+i).build());
        }
        return result;
    }

    public void printAll () {
        List<Dept> deptAll = findAll();
        for (Dept dept: deptAll) {
            log.info(">>> 部门 {}", dept);
        }
    }
}

用户 通过调用部门,进行部门的打印

@Slf4j
public class UserManager {

    public List<User> findAll() {
        List<User> result = new ArrayList<>();

        for (int i = 0;i< 5;i++) {
            result.add(User.builder().id(i+1).name("随机名称"+i).build());
        }

        return result;
    }


    public void printAll2 (NewDeptManager newDeptManager) {
        newDeptManager.printAll();

        List<User> userAll = findAll();
        for (User user: userAll) {
            log.info(">>> 员工 {}", user);
        }
    }
}

测试方法:

@Test
    public void testTwo() {
        UserManager userManager = new UserManager();
        userManager.printAll2(new NewDeptManager());
    }

image-20230612210507278

七.三 注意事项

  1. 迪米特法则的核心是降低类之间的耦合。
  2. 由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)的耦合关系,并不是要求完全没有依赖关系。

八. 合成复用原则

八.一 基本介绍

尽量使用合成/聚合的方式,而不是使用 继承

  1. 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
  2. 针对接口编程,而不是针对实现编程
  3. 为了交互对象之间的松耦合设计而努力

八.二 代码实例

八.三 注意事项


本章节的代码放置在 github 上:

https://github.com/yuejianli/DesignPattern/tree/develop/Principle


谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

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

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

相关文章

【项目】接入飞书平台

前言 项目有和飞书打通的需求&#xff0c;因为是第一次打通&#xff0c;摸索过程还是花了些时间的&#xff0c;现在相关笔记分享给大家。 步骤 1、熟悉开发文档 熟悉飞书的开发文档&#xff1a;开发文档 &#xff0c;找到你需要的接口&#xff0c;拿我为例&#xff0c;我需…

长生的秘密:肠道菌群代谢组学

欲遂长生志&#xff0c;但求千金方。长生不老是人类文明历程中苦苦追寻的目标之一&#xff0c;影响人类寿命的因素也复杂多样&#xff0c;包括但不限于遗传因素如性别、线粒体状态、染色体稳定性、端粒长短、疾病、干细胞活性&#xff1b;环境因素如肠道微生物、饮食、运动、空…

如何解决“RuntimeError: CUDA Out of memory”问题

当遇到这个问题时,你可以尝试一下这些建议,按代码更改的顺序递增: 减少“batch_size” 降低精度 按照错误说的做 清除缓存 修改模型/训练 在这些选项中,如果你使用的是预训练模型,则最容易和最有可能解决问题的选项是第一个。 修改batchsize 如果你是在运行现成的代码或…

页面置换算法的模拟与比较

前言 在计算机操作系统中&#xff0c;页面置换算法是虚拟存储管理中的重要环节。通过对页面置换算法的模拟实验&#xff0c;我们可以更深入地理解虚拟存储技术&#xff0c;并比较不同算法在请求页式虚拟存储管理中的优劣。 随着计算机系统和应用程序的日益复杂&#xff0c;内存…

技术管理方法论

今天来跟大家分享一下我对于技术管理的理解。先介绍一下对于管理最普遍的认识&#xff0c;我们每一个人在公司里面都有两种类型的角色&#xff0c;一种是通过个人的能力和产出来实现组织利益的最大化&#xff0c;另外一类人就是通过管理使得一群人产出结果最大化。 也就是我们…

阿里P8传授的80K+星的MySQL笔记助我修行,一周快速进阶

MySQL 是最流行的关系型数据库之一&#xff0c;广泛的应用在各个领域。下面这些问题对于程序员的你来说应该很常见&#xff0c;来看看你面对这些问题是否会胆怯? MySQL数据库作发布系统的存储&#xff0c;一天五万条以上的增量&#xff0c;预计运维三年,怎么优化&#xff1f; …

Linux防火墙学习笔记8

iptables的白名单和黑名单&#xff1a; iptables -t filter -I INPUT -s 192.168.2.20 -p tcp --dport 80 -j DROP 之前内网的机器可以访问到80端口&#xff0c;现在添加了这条规则&#xff0c;那么就192.168.2.10这个用户就不能访问了。 案例&#xff1a;白名单&#xff1a;…

系列二、RuoYi前后端分离(登录密码加密去除公钥)

一、问题描述 系列一虽然实现了登录时密码加密&#xff0c;但是/getPublicKey返回的结果中&#xff0c;把私钥也返回了&#xff0c;这样显然是不合理的&#xff0c;如下&#xff1a; 二、后端代码修改 2.1、RSAUtil package com.tssl.business.utils;import org.apache.comm…

STM32单片机蓝牙APP LORA无线远程火灾报警温度烟雾监控系统

实践制作DIY- GC0145蓝牙APP LORA无线远程火灾报警 基于STM32单片机设计---蓝牙APP LORA无线远程火灾报警 二、功能介绍&#xff1a; 1个主机&#xff1a;STM32F103C系列单片机LCD1602显示器蜂鸣器 LORA无线模块3个按键&#xff08;设置、加、减&#xff09;HC-05蓝牙模块&…

Node包管理工具

包管理工具 package代表了一组特定功能的源码集合。 管理包的应用软件&#xff0c;可以对包进行下载安装&#xff0c;更新&#xff0c;删除&#xff0c;上传等操作 借助包管理工具&#xff0c;可以快速开发项目&#xff0c;提高开发效率 前端常用包管理工具 npm Node Pack…

【算法系列 | 3】深入解析排序算法之——选择排序

序言 你只管努力&#xff0c;其他交给时间&#xff0c;时间会证明一切。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用来标记二级论点 决定开一个算法专栏&#xff0c;希望能帮助大…

基于Hexo和Butterfly创建个人技术博客,(3) 创建博客文章及文章模板配置

Hexo官司网查看 这里 笔者个人站查看 这里 特别说明&#xff1a; hexo博客站点发布的文件全是静态文件&#xff0c;没有任何后台服务。博文的发布过程是&#xff1a;1、在本地用hexo new命令创建.md文件----2、经hexo g命令生成.html文件-----3、再通过hexo d命令发布到远程主机…

知网CN期刊《才智》简介及投稿邮箱

《才智》杂志成立于2001年&#xff0c;隶属吉林省人事厅&#xff0c;是经国家新闻出版总署批准的&#xff0c;吉林省人事系统唯一一本面向全国公开发行的杂志。是一本专业发表各类论文评定职称的省级理论性杂志。以挖掘各行各业拔尖人才、促进科教兴国、振兴人才市场为己任&…

python套接字(二):实现一个服务器和多客户端连接

文章目录 前言一、问题二、实现一个服务器连接多个客户端1、问题分析2、代码实现a、服务器端b、客户端 3、运行 前言 在上一篇博客python套接字(一)&#xff1a;socket的使用简单说明了一下套接字的使用&#xff0c;也实现了使用套接字来传输消息&#xff0c;但是也有一个问题…

深度学习应用篇-推荐系统[12]:经典模型-DeepFM模型、DSSM模型召回排序策略以及和其他模型对比

【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍&#xff1a;【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化…

JavaWeb笔记(五)

JavaWeb后端 经过前面的学习&#xff0c;现在终于可以正式进入到后端的学习当中&#xff0c;不过&#xff0c;我们还是需要再系统地讲解一下HTTP通信基础知识&#xff0c;它是我们学习JavaWeb的基础知识&#xff0c;我们之前已经学习过TCP通信&#xff0c;而HTTP实际上是基于T…

使用SonarLint在开发阶段提高代码质量

使用SonarLint在开发阶段提高代码质量 SonarLint是什么 SonarLint是一个免费的IDE插件&#xff0c;是一个代码质量工具。 它可以在我们编写代码的时候&#xff0c;就帮我我们发现问题并提醒我们。可以帮助我们养成良好的代码习惯。 它支持5000条规则&#xff0c;可以帮助我…

如何在Microsoft Excel中使用MATCH查找值的位置

当你需要在电子表格中查找值的确切位置时,可以使用 Excel 中的 MATCH 函数。这样可以避免你手动搜索可能需要参考的位置或其他公式。 MATCH 函数通常与 INDEX 函数一起用作高级查找。但在这里,我们将介绍如何单独使用 MATCH 来找到价值所在。 一、Excel中的MATCH函数是什么 …

11. Synchronized与锁升级

11.1 面试题 ● 谈谈你对Synchronized的理解 ● Sychronized的锁升级你聊聊 ● Synchronized实现原理&#xff0c;monitor对象什么时候生成的&#xff1f;知道monitor的monitorenter和monitorexit这两个是怎么保证同步的嘛&#xff1f;或者说这两个操作计算机底层是如何执行的 …

【企业业务架构】LEANIX : 业务能力

业务能力是组织执行核心功能所需的能力、材料和专业知识的表达或发声。企业架构师使用业务能力来说明业务的总体需求&#xff0c;以便更好地制定满足这些业务需求的 IT 解决方案。 目录 介绍业务能力建模您可以通过业务能力映射实现什么&#xff1f;并购管理IT风险管理创新管理…