详解23种设计模式——第一部分:概述+创建型模式

目录

1. 概述

2. 创建型模式

2.1 简单(静态)工厂模式

2.1.1 介绍

2.1.2 实现

2.2 工厂模式

2.3 抽象工厂模式

2.4 单例模式

2.4.1 饿汉模式

2.4.2 懒汉模式

2.4.3 线程安全的懒汉式

2.4.4 DCL单例 - 高性能的懒汉式

2.5 建造者模式

2.6 原型模式

2.7 创建型模式总结


1. 概述

设计模式(Design Pattern)是前辈们经过相当长的一段时间的试验和错误总结出来的,是软件开发过程中面临的通用问题的解决方案。这些解决方案使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性

设计模式的优点:

  • ① 可以提高程序员的思维能力、编程能力和设计能力。
  • ② 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
  • ③ 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。

有一些重要的设计原则在开篇和大家分享下,这些原则将贯通全文:

  • 针对接口编程,而不是针对实现编程。这个很重要,也是优雅的、可扩展的代码的第一步。
  • 职责单一原则。每个类都应该只有一个单一的功能,并且该功能应该由这个类完全封装起来。
  • 对修改关闭,对扩展开放。对修改关闭是说,我们辛辛苦苦加班写出来的代码,该实现的功能和该修复的 bug 都完成了,别人可不能说改就改;对扩展开放就比较好理解了,也就是说在我们写好的代码基础上,很容易实现扩展。

设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结,其中最出名的当属 Gang of Four(GoF)的分类了,他们将设计模式分类为 23 种经典的模式,根据用途我们又可以分为三大类,分别为创建型模式(5种)结构型模式(7种)行为型模式(11种)

(1)创建型模式

  1. 简单(静态)工厂模式:一个工厂类根据传入的参量决定创建出那一种产品类的实例。(该模式不属于23种GOF设计模式之一
  2. 工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。
  3. 抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。
  4. 单例模式:某个类只能有一个实例,提供一个全局的访问点。
    1. 饿汉模式
    2. 懒汉模式
  5. 建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。
  6. 原型模式:通过复制现有的实例来创建新的实例。

(2)结构型模式

  1. 外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
  2. 桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
  3. 组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。
  4. 装饰模式:动态的给对象添加新的功能。
  5. 代理模式:为其他对象提供一个代理以便控制这个对象的访问。
  6. 适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。
  7. 亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象。

(3)行为型模式

  1. 模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
  2. 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
  3. 策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
  4. 状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
  5. 观察者模式:对象间的一对多的依赖关系。
  6. 备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
  7. 中介者模式:用一个中介对象来封装一系列的对象交互。
  8. 命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
  9. 访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
  10. 责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
  11. 迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

2. 创建型模式

创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是 new 一个对象,然后 set 相关属性。但是,在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者用的时候。

2.1 简单(静态)工厂模式

2.1.1 介绍

① 一句话来说就是:一个工厂类根据传入的参量决定创建出那一种产品类的实例。因为逻辑实现简单,所以称为简单工厂模式,也因为工厂中的方法一般设置为静态,所以也称为静态工厂,它不属于23种模式

② 简单工厂模式专门定义一个工厂类来负责创建其他类的实例,被创建的实例通常都具有共同的父类,在工厂类中,可以根据参数的不同返回不同类的实例。升级版本简单工厂模式,通过反射根据类的全路径名生成对象。

③ 简单工厂模式就是将这部分创建对象语句分离出来,由工厂类来封装实例化对象的行为,修改时只需要修改类中的操作代码,使用时调用该类不需要考虑实例化对象的行为,使得后期代码维护升级更简单方便,有利于代码的可修改性与可读性。

④ 但是如果增加新的产品的话,需要修改工厂类的判断逻辑,违背开闭原则。

2.1.2 实现

直接上代码:

public class FoodFactory {
    public static Food makeFood(String name) {
        if (name.equals("noodle")) {
            Food noodle = new LanZhouNoodle();
            noodle.addSpicy("more");
            return noodle;
        } else if (name.equals("chicken")) {
            Food chicken = new HuangMenChicken();
            chicken.addCondiment("potato");
            return chicken;
        } else {
            return null;
        }
    }
}

其中,LanZhouNoodle 和 HuangMenChicken 都继承自 Food。

简单地说,简单工厂模式通常就是这样:一个工厂类 XxxFactory,里面有一个静态方法,根据我们不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象

我们强调职责单一原则,一个类只提供一种功能,FoodFactory 的功能就是只要负责生产各种 Food。

2.2 工厂模式

简单工厂模式很简单,如果它能满足我们的需要,我觉得就不要折腾了。之所以需要引入工厂模式,是因为我们往往需要使用两个或两个以上的工厂。

public interface FoodFactory {
    Food makeFood(String name);
}
public class ChineseFoodFactory implements FoodFactory {
    @Override
    public Food makeFood(String name) {
        if (name.equals("A")) {
            return new ChineseFoodA();
        } else if (name.equals("B")) {
            return new ChineseFoodB();
        } else {
            return null;
        }
    }
}
public class AmericanFoodFactory implements FoodFactory {
    @Override
    public Food makeFood(String name) {
        if (name.equals("A")) {
            return new AmericanFoodA();
        } else if (name.equals("B")) {
            return new AmericanFoodB();
        } else {
            return null;
        }
    }
}

其中,ChineseFoodA、ChineseFoodB、AmericanFoodA、AmericanFoodB 都派生自 Food。

客户端调用:

public class APP {
    public static void main(String[] args) {
        // 先选择一个具体的工厂
        FoodFactory factory = new ChineseFoodFactory();
        // 由第一步的工厂产生具体的对象,不同的工厂造出不一样的对象
        Food food = factory.makeFood("A");
    }
}

虽然都是调用 makeFood("A") 制作 A 类食物,但是,不同的工厂生产出来的完全不一样。

第一步,我们需要选取合适的工厂,然后第二步基本上和简单工厂一样。

核心在于,我们需要在第一步选好我们需要的工厂。比如,我们有 LogFactory 接口,实现类有 FileLogFactory 和 KafkaLogFactory,分别对应将日志写入文件和写入 Kafka 中,显然,我们客户端第一步就需要决定到底要实例化 FileLogFactory 还是 KafkaLogFactory,这将决定之后的所有的操作。

虽然简单,不过我也把所有的构件都画到一张图上,这样看着比较清晰:

2.3 抽象工厂模式

当涉及到产品族的时候,就需要引入抽象工厂模式了。

一个经典的例子是造一台电脑。我们先不引入抽象工厂模式,看看怎么实现。

因为电脑是由许多的构件组成的,我们将 CPU 和主板进行抽象,然后 CPU 由 CPUFactory 生产,主板由 MainBoardFactory 生产,然后,我们再将 CPU 和主板搭配起来组合在一起,如下图:

这个时候的客户端调用是这样的:

// 得到 Intel 的 CPU
CPUFactory cpuFactory = new IntelCPUFactory();
CPU cpu = intelCPUFactory.makeCPU();

// 得到 AMD 的主板
MainBoardFactory mainBoardFactory = new AmdMainBoardFactory();
MainBoard mainBoard = mainBoardFactory.make();

// 组装 CPU 和主板
Computer computer = new Computer(cpu, mainBoard);

单独看 CPU 工厂和主板工厂,它们分别是前面我们说的工厂模式。这种方式也容易扩展,因为要给电脑加硬盘的话,只需要加一个 HardDiskFactory 和相应的实现即可,不需要修改现有的工厂。

但是,这种方式有一个问题,那就是如果 Intel 家产的 CPU 和 AMD 产的主板不能兼容使用,那么这代码就容易出错,因为客户端并不知道它们不兼容,也就会错误地出现随意组合。

下面就是我们要说的产品族的概念,它代表了组成某个产品的一系列附件的集合:

当涉及到这种产品族的问题的时候,就需要抽象工厂模式来支持了。我们不再定义 CPU 工厂、主板工厂、硬盘工厂、显示屏工厂等等,我们直接定义电脑工厂,每个电脑工厂负责生产所有的设备,这样能保证肯定不存在兼容问题

这个时候,对于客户端来说,不再需要单独挑选 CPU厂商、主板厂商、硬盘厂商等,直接选择一家品牌工厂,品牌工厂会负责生产所有的东西,而且能保证肯定是兼容可用的。

public static void main(String[] args) {
    // 第一步就要选定一个“大厂”
    ComputerFactory cf = new AmdFactory();
    // 从这个大厂造 CPU
    CPU cpu = cf.makeCPU();
    // 从这个大厂造主板
    MainBoard board = cf.makeMainBoard();
    // 从这个大厂造硬盘
    HardDisk hardDisk = cf.makeHardDisk();

    // 将同一个厂子出来的 CPU、主板、硬盘组装在一起
    Computer result = new Computer(cpu, board, hardDisk);
}

当然,抽象工厂的问题也是显而易见的,比如我们要加个显示器,就需要修改所有的工厂,给所有的工厂都加上制造显示器的方法。这有点违反了对修改关闭,对扩展开放这个设计原则

2.4 单例模式

2.4.1 饿汉模式

饿汉模式最简单

public class Singleton {
    /**
     * static:
     * ①表示共享变量,语意符合
     * ②使得该变量能在getInstance()静态方法中使用
     * final:
     * ①final修饰的变量值不会改变即常量,语意也符合,当然不加final也是可以的
     * ②保证修饰的变量必须在类加载完成时就已经进行赋值。
     * final修饰的变量,前面一般加static
     */
    private static final Singleton singleton = new Singleton();

    /**
     * 私有化构造方法,使外部无法通过构造方法构造除singleton外的类实例
     * 从而达到单例模式控制类实例数目的目的
     */
    private Singleton(){}

    /**
     * 类实例的全局访问方法
     * 因为构造方法以及被私有化,外部不可能通过new对象来调用其中的方法
     * 加上static关键词使得外部可以通过类名直接调用该方法获取类实例
     * @return
     */
    public static Singleton getSingleton() {
        return singleton;
    }
}

说明:

① 优点:一般使用static和final修饰变量(具体作用已经在代码里描述了),只在类加载时才会初始化,以后都不会,线程绝对安全,无锁,效率高

② 缺点:类加载的时候就初始化,不管用不用,都占用空间,会消耗一定的性能(当然很小很小,几乎可以忽略不计,所以这种模式在很多场合十分常用而且十分简单)。

2.4.2 懒汉模式

public class Singleton {
    private static Singleton singleton = null;
    private Singleton(){}
    public static Singleton getSingleton() {
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

说明:

① 优点:在外部需要使用的时候才进行实例化,不使用的时候不会占用空间

② 缺点:线程不安全。看上去,这段代码没什么明显问题,但它不是线程安全的。假设当前有N个线程同时调用getInstance()方法,由于当前还没有对象生成,所以一部分同时都进入if语句new Singleton(),那么就会由多个线程创建多个多个user对象

2.4.3 线程安全的懒汉式

public class Singleton {
    private static Singleton singleton;
    private Singleton(){};
    private static synchronized Singleton getSingleton(){
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

说明:

① 优点:解决了懒汉式线程不安全的问题

② 缺点:线程阻塞,影响性能

2.4.4 DCL单例 - 高性能的懒汉式

public class Singleton {
 /*volatile在这里发挥的作用是:禁止指令重排序(编译器和处理器为了优化程序性能
    * 而对指令序列进行排序的一种手段。)
    * singleton = new Singleton();这句代码是非原子性操作可分为三行伪代码
    * a:memory = allocate() //分配内存,在jvm堆中分配一段区域
    * b:ctorInstanc(memory) //初始化对象,在jvm堆中的内存中实例化对象
    * c:instance = memory //赋值,设置instance指向刚分配的内存地址
    * 上面的代码在编译运行时,可能会出现重排序从a-b-c排序为a-c-b。
    * 重排序是为了优化性能,但是不管怎么重排序,在单线程下程序的执行结果不能被改变
    * 保证最终一致性。而在多线程环境下,可能发生重排序,会影响结果。
    * ①若A线程执行到代码singleton = new Singleton()时;
    * ②同时若B线程进来执行到代码到第一层检查if (singleton == null)
    * ③当cpu切换到A线程执行代码singleton = new Singleton();时发生了指令重排序,
    * 执行了a-b,没有执行c,此时的singleton对象只有地址,没有内容。然后cpu又切换到了B线程,
    * 这时singleton == null为false(==比较的是内存地址),
    * 则代码会直接执行到了return,返回一个未初始化的对象(只有地址,没有内容)。
    * */
    private volatile static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getSingleton() {
        /*第一层检查,检查是否有引用指向对象,高并发情况下会有多个线程同时进入
        * ①当多个线程第一次进入,所有线程都进入if语句
        * ②当多个线程第二次进入,因为singleton已经不为null,因此所有线程都不会进入if语句,
        * 即不会执行锁,从而也就不会因为锁而阻塞,避免锁竞争*/
        if (singleton == null) {
            /*第一层锁,保证只有一个线程进入,
            * ①多个线程第一次进入的时候,只有一个线程会进入,其他线程处于阻塞状态
            * 当进入的线程创建完对象出去之后,其他线程又会进入创建对象,所以有了第二次if检查
            * ②多个线程第二次是进入不到这里的,因为已被第一次if检查拦截*/
            synchronized (Singleton.class) {
                /*第二层检查,防止除了进入的第一个线程的其他线程重复创建对象*/
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

说明:代码注释已详细讲解volatile在该单例模式的作用,已经双重锁的作用。

① 优点:解决了线程阻塞的问题。

② 缺点:多个线程第一次进入的时候会造成大量的线程阻塞,代码不够优雅。

静态内部类的方式

public class Singleton {
    private Singleton(){}
    private static class LayzInner{
        private static Singleton singleton = new Singleton();
    }
    public static Singleton getSingleton(){
        return LayzInner.singleton;
    }
}

说明:

① 优点:第一次类创建的时候加载,避免了内存浪费,不存在阻塞问题,线程安全,唯一性

② 缺点:序列化-漏洞:反射,会破坏内部类单例模式

2.5 建造者模式

经常碰见的 XxxBuilder 的类,通常都是建造者模式的产物。建造者模式其实有很多的变种,但是对于客户端来说,我们的使用通常都是一个模式的:

Food food = new FoodBuilder().a().b().c().build();
Food food = Food.builder().a().b().c().build();

套路就是先 new 一个 Builder,然后可以链式地调用一堆方法,最后再调用一次 build() 方法,我们需要的对象就有了。

来一个中规中矩的建造者模式:

class User {
    // 下面是“一堆”的属性
    private String name;
    private String password;
    private String nickName;
    private int age;

    // 构造方法私有化,不然客户端就会直接调用构造方法了
    private User(String name, String password, String nickName, int age) {
        this.name = name;
        this.password = password;
        this.nickName = nickName;
        this.age = age;
    }
    // 静态方法,用于生成一个 Builder,这个不一定要有,不过写这个方法是一个很好的习惯,
    // 有些代码要求别人写 new User.UserBuilder().a()...build() 看上去就没那么好
    public static UserBuilder builder() {
        return new UserBuilder();
    }

    public static class UserBuilder {
        // 下面是和 User 一模一样的一堆属性
        private String  name;
        private String password;
        private String nickName;
        private int age;

        private UserBuilder() {
        }

        // 链式调用设置各个属性值,返回 this,即 UserBuilder
        public UserBuilder name(String name) {
            this.name = name;
            return this;
        }

        public UserBuilder password(String password) {
            this.password = password;
            return this;
        }

        public UserBuilder nickName(String nickName) {
            this.nickName = nickName;
            return this;
        }

        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }

        // build() 方法负责将 UserBuilder 中设置好的属性“复制”到 User 中。
        // 当然,可以在 “复制” 之前做点检验
        public User build() {
            if (name == null || password == null) {
                throw new RuntimeException("用户名和密码必填");
            }
            if (age <= 0 || age >= 150) {
                throw new RuntimeException("年龄不合法");
            }
            // 还可以做赋予”默认值“的功能
            if (nickName == null) {
                nickName = name;
            }
            return new User(name, password, nickName, age);
        }
    }
}

核心是:先把所有的属性都设置给 Builder,然后 build() 方法的时候,将这些属性复制给实际产生的对象

看看客户端的调用:

public class APP {
    public static void main(String[] args) {
        User d = User.builder().name("foo").password("pAss12345").age(25).build();
    }
}

说实话,建造者模式的链式写法很吸引人,但是,多写了很多“无用”的 builder 的代码,感觉这个模式没什么用。不过,当属性很多,而且有些必填,有些选填的时候,这个模式会使代码清晰很多。我们可以在 Builder 的构造方法中强制让调用者提供必填字段,还有,在 build() 方法中校验各个参数比在 User 的构造方法中校验,代码要优雅一些。

题外话,强烈建议读者使用 lombok,用了 lombok 以后,上面的一大堆代码会变成如下这样:

@Builder
class User {
    private String  name;
    private String password;
    private String nickName;
    private int age;
}

当然,如果你只是想要链式写法,不想要建造者模式,有个很简单的办法,User 的 getter 方法不变,所有的 setter 方法都让其 return this 就可以了,然后就可以像下面这样调用:

User user = new User().setName("").setPassword("").setAge(20);

很多人是这么用的,但是这种写法不太优雅,不是很推荐使用。

2.6 原型模式

在某些情况下,需要创建对象的副本,但复制一个对象的成本可能很高,或者希望避免与对象的具体类耦合。例如,当创建对象的过程较为复杂,或者对象包含大量共享的状态时,使用常规的创建方法可能会导致性能下降。

原型模式的解决方案是通过复制现有对象来创建新对象,而不是从头开始构建。这允许我们以更高效的方式创建新对象,同时避免了与对象类的直接耦合。核心概念是在原型对象的基础上进行克隆,使得新对象具有与原型相同的初始状态。

原型模式很简单:有一个原型实例,基于这个原型实例产生新的实例,也就是“克隆”了

Object 类中有一个 clone() 方法,它用于生成一个新的对象,当然,如果我们要调用这个方法,java 要求我们的类必须先实现 Cloneable 接口,此接口没有定义任何方法,但是不这么做的话,在 clone() 的时候,会抛出 CloneNotSupportedException 异常。

protected native Object clone() throws CloneNotSupportedException;

注意事项: 

  • 深克隆问题:原型模式默认进行浅克隆,即复制对象本身和其引用。如果对象内部包含其他对象的引用,可能需要实现深克隆来复制整个对象结构。
  • 克隆方法的实现:某些对象可能不容易进行克隆,特别是涉及到文件、网络连接等资源的情况。

Java 的克隆是浅克隆,碰到对象引用的时候,克隆出来的对象和原对象中的引用将指向同一个对象。通常实现深克隆的方法是将对象进行序列化,然后再进行反序列化。

总之,原型模式是一种在需要创建对象副本时非常有用的设计模式,它提供了一种灵活且高效的方法来处理对象的复制需求。

2.7 创建型模式总结

创建型模式总体上比较简单,它们的作用就是为了产生实例对象,算是各种工作的第一步了,因为我们写的是面向对象的代码,所以我们第一步当然是需要创建一个对象了。

总结:

  • 工厂模式在简单工厂模式的基础上增加了选择工厂的维度,需要第一步选择合适的工厂;
  • 抽象工厂模式有产品族的概念,如果各个产品是存在兼容性问题的,就要用抽象工厂模式;
  • 单例模式为了保证全局使用的是同一对象,一方面是安全性考虑,一方面是为了节省资源;
  • 建造者模式专门对付属性很多的那种类,为了让代码更优美;
  • 原型模式用得比较少,了解和 Object 类中的 clone() 方法相关的知识即可。

后续待更新......

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【进阶OpenCV】 (19)-- Dlib库 --人脸表情识别

文章目录 表情识别一、原理二、代码实现1. 摄像头前预处理2. 计算嘴唇变化3. 绘制嘴唇轮廓4. 显示结果5. 完整代码展示 总结 表情识别 目标&#xff1a;识别人物的喜悦状态。 一、原理 我们在对一张人脸图片进行关键点定位后&#xff0c;得到每个关键点的位置&#xff1a; 比…

go压缩的使用

基础&#xff1a;使用go创建一个zip func base(path string) {// 创建 zip 文件zipFile, err : os.Create("test.zip")if err ! nil {panic(err)}defer zipFile.Close()// 创建一个新的 *Writer 对象zipWriter : zip.NewWriter(zipFile)defer zipWriter.Close()// 创…

【Linux】————动静态库

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;Linux 创作时间 &#xff1a;2024年10月22日 一&#xff0e;库的定义 什么是库&#xff0c;在windows平台和linux平台下都大量存在着库。 本质上来说库是一种可执行代码的二进制形式&#xff0c;可以被操作系统载…

渗透实战 JS文件怎么利用

1.前言 关于JS在渗透测试中的关键作用&#xff0c;想必不用过多强调&#xff0c;在互联网上也有许多从JS中找到敏感信息从而拿下关键系统的案例。大部分师傅喜欢使用findsomething之类的浏览器插件&#xff0c;也有使用诸如Unexpected.information以及APIFinder之类的Burp插件…

ES6 Promise的用法

学习链接&#xff1a;ES6 Promise的用法&#xff0c;ES7 async/await异步处理同步化&#xff0c;异步处理进化史_哔哩哔哩_bilibili 一、同步与异步区别 1.JavaScript代码是单线程的程序&#xff0c;即通过一行一行代码顺序执行&#xff0c;即同步概念。 2.若处理一些简短、…

算法魅力-双指针的实战

目录 1.双指针的介绍 1. 左右指针&#xff08;对撞指针&#xff09; 2. 快慢指针 2.题目练习讲解 2.1 移动零 算法思路 代码展示 画图效果效果 2.2 复写零 算法思路 代码展示 2.3 快乐数 算法思路 代码展示 2.4 盛最多水的容器 算法思路 代码展示 结束语 1.双指针的…

宝塔PHP8.1安装fileinfo拓展失败解决办法

在宝塔面板中安装PHP8.1后&#xff0c;安装fileinfo扩展一直安装不上&#xff0c;查看日志有报错&#xff0c;于是手动来安装也报错。 宝塔报错&#xff1a; 手动命令行编译安装同&#xff0c;也有报错 cd /www/server/php/81/src/ext/fileinfo/ make distclean ./configure …

【用74ls194实现1000-0100-0010-0001转换】2022-5-13

试用74194附加门电路设计1011011010序列发生器&#xff0c;并用示波器观察。要求&#xff1a;&#xff08;1&#xff09;写出设计过程&#xff1b;&#xff08;2&#xff09;画出电路图。 2、用multisim软件仿真实现上述序列信号发生器&#xff0c;CP频率为1KHz&#xff0c;用示…

【HarmonyOS】应用实现APP国际化多语言切换

【HarmonyOS】应用实现APP国际化多语言切换 前言 在鸿蒙中应用国际化处理&#xff0c;与Android和IOS基本一致&#xff0c;都是通过JSON配置不同的语言文本内容。在UI展示时&#xff0c;使用JSON配置的字段key进行调用&#xff0c;系统选择对应语言文本内容。 跟随系统多语言…

【scene_manager】与 MoveIt 机器人的规划场景进行交互

scene_manager Scene Manager包是由 Robotnik 创建的 ROS 包&#xff0c;旨在帮助构建和与 MoveIt 机器人的规划场景进行交互。 背景信息 MoveIt 规划场景 是一个用于存储机器人周围世界的表示&#xff08;外部碰撞&#xff09;以及机器人自身状态&#xff08;内部碰撞和当…

rollup.js 插件实现原理与自定义

Rollup.js 是一个JavaScript模块打包器&#xff0c;它主要用于将小块代码编译成大块复杂的库或应用程序。相较于Webpack&#xff0c;Rollup更专注于代码的ES模块转换和优化&#xff0c;特别适合构建库或者那些对代码体积、执行效率有严格要求的应用。Rollup的核心特性之一就是它…

NETSH端口转发

NETSH介绍 netsh是windows系统自带命令行程序&#xff0c;攻击者无需上传第三方工具即可利用netsh程序可进行端口转 发操作&#xff0c;可将内网中其他服务器的端口转发至本地访问运行这个工具需要管理员的权限 实验场景 现在有如下的网络&#xff0c;电脑A是攻击机器&#x…

衡石分析平台系统分析人员手册-仪表盘控件概述

控件​ 控件是仪表盘的基本组成单位。控件种类很多&#xff0c;有展示分析数据的图表类类控件&#xff0c;有展示图片、文字的展示类控件&#xff0c;还有可导出数据、刷新数据、过滤数据等功能类控件。一个完整的仪表盘由多种不同功能的控件构成。 控件类型​ 根据控件是否展…

10月18日笔记(基于系统服务的权限提升)

系统内核漏洞提权 当目标系统存在该漏洞且没有更新安全补丁时&#xff0c;利用已知的系统内核漏洞进行提权&#xff0c;测试人员往往可以获得系统级别的访问权限。 查找系统潜在漏洞 手动寻找可用漏洞 在目标主机上执行以下命令&#xff0c;查看已安装的系统补丁。 system…

详解Java之Spring MVC篇一

目录 Spring MVC 官方介绍 MVC RequestMapping 传递参数 无参数 单个参数 针对String类型 针对Integer类型 针对int类型 针对自定义类型 多个参数 参数重命名 参数强制一致 参数不强制一致 传递数组 ​编辑传递List ​编辑 传递JSON ​编辑 从路径中获取参…

树上启发式合并(详解)

核心思想 借用了一个节点到根的路径上轻边个数不会超过logn条。 故重节点保留&#xff0c;轻节点删去&#xff0c;多重统计。 实际复杂度&#xff08;nlogn&#xff09; 例题 Lomsat gelral - 洛谷 AC 代码 #include<bits/stdc.h> #define int long long using na…

无需扩散,下一个token预测直达AGI!

目录 简单概括1 背景知识方法数据视觉 Tokenizer架构预训练 实验结果视频生成未来预测 简单概括 虽然&#xff0c;下一token预测已在大语言模型领域实现了ChatGPT等突破&#xff0c;但是在多模态模型中的适用性仍不明确&#xff0c;多模态任务仍然由扩散模型&#xff08;如Sta…

大规模创新类竞赛评审方案的建模与研究

随着科技的发展和教育制度的改革&#xff0c;近年来涌现出一批以“创新”为主题的竞赛项目。这类竞赛的运行模式为&#xff0c;参赛队伍提交文档、视频或幻灯片等文本形式的作品&#xff0c;专家对参赛队伍提交的作品评阅判分&#xff0c;一份作品将由多位专家独立进行评阅打分…

19.面试算法-树的深度优先遍历(一)

1. 深入理解前中后序遍历 深度优先遍历有前中后三种情况&#xff0c;大部分人看过之后就能写出来&#xff0c;很遗憾大部分只是背下来的&#xff0c;稍微变换一下就废了。 我们再从二叉树的角度看递归&#xff0c;每次遇到递归&#xff0c;都按照前面说的四步来写&#xff0c…

从壹开始解读Yolov11【源码研读系列】——cfg:模型配置加载功能

目录 一、模型配置操作&#xff1a;cfg.__init__.py 1.cfg.cfg2dict&#xff1a;yaml转字典 2.cfg.get_cfg&#xff1a;读取覆盖配置 3.cfg全局配置参数查询表 ①*基础参数配置&#xff1a; ②*训练参数配置&#xff1a; ③验证测试参数配置&#xff1a; ④*预测参数配置&…