设计模式之创建型模式(单例、工厂方法、抽象工厂、原型、建造者)

news2024/11/26 10:48:45

文章目录

    • 一、设计模式
    • 二、设计模式的六大原则
    • 三、设计模式分类
    • 四、单例设计模式
    • 五、工厂方法
    • 六、抽象工厂
    • 七、原型模式
    • 八、建造者模式

一、设计模式

设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。

二、设计模式的六大原则

  • 开闭原则(Open Close Principle)
    开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

  • 里氏代换原则(Liskov Substitution Principle)
    里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

  • 依赖倒转原则(Dependence Inversion Principle)
    这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体

  • 接口隔离原则(Interface Segregation Principle)
    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

  • 迪米特法则,又称最少知道原则(Demeter Principle)
    最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

  • 合成复用原则(Composite Reuse Principle)
    合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。

三、设计模式分类

设计模式常见的有23类,分为创建型结构型行为型

  • 创建型模式主要用于创建对象,为设计类实例化对象做指导。

  • 结构型主要处理类或对象之间的组合,为如何设计类以形成更大的结构做指导。

  • 行为型主要描述类或对象的交互和职责的分类,为类的交互和职责分类做指导。

四、单例设计模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例的实现方式也有多种,包括:饿汉式、懒汉式、静态内部方式、枚举单例、双重检测锁方式 。

  1. 懒汉式

类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。
在设计单例时,首先需要私有化无参构造函数,避免使用 new 创建对象。

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

这种情况如果并发获取对象的时候,因为加了synchronized 锁,会抢夺锁导致一些性能的下降。

  1. 饿汉式

类初始化时,会立即加载该对象,线程天生安全,调用效率高,这种方式比较常用,但容易产生垃圾对象。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance() 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    	return instance;  
    }  
}

这种方式在加载时就创建好了对象,相对于上面的懒汉式,性能会更高。但是对于一些不常用的类,在一开始就加载好,浪费内存,容易产生垃圾对象。

  1. 双检锁/双重校验锁 DCL

上面可以看出,使用懒汉式由于在方法上加了synchronized 会导致请求排队,使用饿汉式,又会有可能造成浪费内存产生垃圾对象,带着问题来看下双重校验锁机制。

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

这种形式,在获取对象时先判断有无实例,有的话就直接返回,相对于懒汉式消除了锁争夺等待。如果没有被实例化,就使用synchronized 锁住当前对象,那么,都锁住了为什么在里面还要再判断一次是否有实例呢,这是因为假如并发有两个线程获取对象,都判断为null,那下面就会争夺锁,抢到锁的就可以安心的实例化对象了,当创建好释放锁后,另一个线程走下去再创建对象不就创建重复了嘛,上一个线程已经创建好了呀,所以在这里再判断一次,就是为了在刚开始创建对象的时候防止并发重复实例化对象。解决了懒汉式和饿汉式的弊端。

  1. 静态内部类

上面双重校验锁机制似乎就已经很好了,但还有没有办法解决懒汉式和饿汉式的弊端呢,那就是静态内部类;
这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

public class Singleton {  

    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    
    private Singleton (){}  
  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE;  
    }  
}
  • 优势:解决懒汉式和饿汉式的弊端
  • 劣势:需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象。
  1. 枚举

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法,因为枚举本身是单例的。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}
public class Singleton {
	public static Singleton getInstance() {
		return SingletonEnum.INSTANCE.getInstance();
	}

	private static enum SingletonEnum{
		INSTANCE;
		private Singleton instance;

		private SingletonEnum() {
			instance= new Singleton ();
		}
		
		public Singleton getInstance() {
			return instance;
		}
	}
Singleton s = Singleton.getInstance();
  • 优点:实现简单、枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞
  • 缺点:没有延迟加载

五、工厂方法

工厂方法,使用工厂可以像使用人员屏蔽对象创建的细节,使用者无需指定具体的类即可使用功能,达到信息隐蔽的作用,便于后期的维护,修改和扩展

在看工厂方法前还有一类是 简单工厂,相当于是一个工厂中有各种产品,创建在一个类中,客户无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。
在这里插入图片描述

比如我们在聚合支付的时候有,微信支付和支付宝支付,在不同的选择要获取不同的对象,如果用简单方法来实现的话:

  1. 先定义统一的支付抽象类,定义支付和取消支付抽象方法
public interface PayInterFace {
    //支付
    void pay();
    //取消支付
    void cancelPay();
}
  1. 定义微信支付类
public class WxPay implements PayInterFace {
    @Override
    public void pay() {
        System.out.println("微信支付中...");
    }

    @Override
    public void cancelPay() {
        System.out.println("微信取消支付!");
    }
}
  1. 定义支付宝类
public class ZfbPay implements PayInterFace {
    @Override
    public void pay() {
        System.out.println("支付宝支付中...");
    }

    @Override
    public void cancelPay() {
        System.out.println("支付宝取消支付!");
    }
}
  1. 创建简单工厂
public class SimplePayFactory {
    public static PayInterFace getPay(String payType) {
        if (Objects.equals(payType, "wx")) {
            return new WxPay();
        } else if (Objects.equals(payType, "zfb")) {
            return new ZfbPay();
        } else {
            return null;
        }
    }
}
  1. 使用
public class demo {
    public static void main(String[] args) {
        PayInterFace pay = SimplePayFactory.getPay("wx");
        pay.pay();
        pay.cancelPay();
        PayInterFace pay1 = SimplePayFactory.getPay("zfb");
        pay1.pay();
        pay1.cancelPay();
    }
}

在这里插入图片描述

通过传入不同的标识获取不同的对象,且无需指定具体对象,让使用者无需关注创建对象的细节,增加对象需要在工厂中添加对应的实例化程序,当类型过多时不利于系统的扩展维护。

那下面来看下工厂方法的做法:

  1. 在上面的基础上,再建立 支付工厂接口:
public interface PayFactory {
    PayInterFace getPay();
}
  1. 建立微信支付工厂
public class WxPayFactory implements PayFactory {
   @Override
   public PayInterFace getPay() {
       return new WxPay();
   }
}
  1. 建立支付宝工厂
public class ZfbPayFactory implements PayFactory {
    @Override
    public PayInterFace getPay() {
        return new ZfbPay();
    }
}
  1. 使用
public class demo {
    public static void main(String[] args) {
        PayInterFace pay = new WxPayFactory().getPay();
        pay.pay();
        pay.cancelPay();

        PayInterFace pay1 = new ZfbPayFactory().getPay();
        pay1.pay();
        pay1.cancelPay();
    }
}

这种方式同样屏蔽了对象建立的细节,且无需指定具体对象,但相对于上面的简单工厂,更易于做扩展,比如要引入新的支付,只需要创建新的工厂并实现PayFactory 即可,更利于系统的扩展。

六、抽象工厂

上面学习了简单工厂工厂方法,下面来看下抽象工厂
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

在上面的场景中,加入支付后需要扣除库存,就需要引入库存工厂,并调用扣除库存的方法,库存工厂和支付工厂貌似不相干,但在抽象工厂中,抽象工厂相当于一个公司,库存和支付相当与部门,相对于公司来看,支付和库存都是公司下的部门,是一个整体。所以抽象工厂就是定义一个产品组,可以完成一系列的过程,而不是单一的活动。

下面使用抽象工厂定义支付后扣除库存的设计:

  1. 定义库存操作接口
public interface RepertoryInterFace {
    void deductRepertory();
}
  1. 以苹果为例,定义具体库存为苹果的库存
public class AppleRepertory implements RepertoryInterFace {
    @Override
    public void deductRepertory() {
        System.out.println("苹果库存 扣除库存...");
    }
}
  1. 定义抽象工厂,定义支付和扣库存抽象方法
public interface BuyAbstractFactory {
    //支付
    PayInterFace pay();
    //扣库存
    RepertoryInterFace deductRepertory();
}
  1. 定义抽象工厂实现,这里演示效果,直接采用微信支付对象
public class BuyAbstractFactoryImpl implements BuyAbstractFactory {

    @Override
    public PayInterFace pay() {
        return new WxPay();
    }

    @Override
    public RepertoryInterFace deductRepertory() {
        return new AppleRepertory();
    }
}
  1. 使用
public class demo {
    public static void main(String[] args) {
        BuyAbstractFactory factory = new BuyAbstractFactoryImpl();
        factory.pay().pay();
        factory.deductRepertory().deductRepertory();
    }
}

在这里插入图片描述

使用抽象工厂方式,颗粒化程度大,让使用者无需知道整个工作组的对象,即可完成一连串的操作,对可扩展性,可修改修都很友好

七、原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。它提供了一种创建对象的最佳方式
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。
例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

例如:

创建一个可复制的支付类:

public class WxPay implements Cloneable{
    public WxPay(){
        System.out.println("创建支付对象");
    }
    public WxPay clone() throws CloneNotSupportedException {
        System.out.println("复制对象!");
        return (WxPay) super.clone();
    }
}

使用:

public class demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        WxPay pay = new WxPay();
        WxPay pay1 = pay.clone();
        System.out.println(pay);
        System.out.println(pay1);
    }
}

在这里插入图片描述

可以看到,创建了两个不同的对象,只进行了一次构件函数的执行,当直接创建对象的代价比较大时,就可以采用这种模式。

另外,原型的拷贝又有浅拷贝深拷贝两个层次,上面的方式就是浅拷贝,只把当前对象做了拷贝,如果对象中有其他对象的引用,就不会进行拷贝,修改任意一个对象中的引用,对其他都会有影响

比如上面的WxPay类做如下修改:

  1. 先定义其他操作类
public class OtherOperation {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  1. 再修改WxPay类
public class WxPay implements Cloneable {

    private OtherOperation otherOperation = new OtherOperation();

    public WxPay() {
        System.out.println("创建支付对象");
    }

    public WxPay clone() throws CloneNotSupportedException {
        System.out.println("复制对象!");
        return (WxPay) super.clone();
    }

    public OtherOperation getOperation() {
        return otherOperation;
    }

    public String getName() {
        return otherOperation.getName();
    }

}
  1. 演示
public class demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        WxPay pay = new WxPay();
        WxPay pay1 = pay.clone();
        System.out.println(pay);
        System.out.println(pay1);
        System.out.println(pay.getOperation());
        System.out.println(pay1.getOperation());
        pay.getOperation().setName("abc");
        System.out.println(pay1.getOperation().getName());
    }
}

在这里插入图片描述

上面可以看出,WxPay确实是复制出了一个实例,但是WxPay里面的OtherOperation实例没有复制,还只向同一个地址,导致只要修改任意一个对象中的OtherOperation对其他实例都会有影响。

上面已经看出默认是浅拷贝,但有的时候,我们又需要其中引用的对象也要为新实例,那怎么做呢,下面就来看下深拷贝的实现方式:

深拷贝实现方式 1:重写 clone 方法来实现深拷贝
深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)

实现方式 1,重写 clone 方法来实现深拷贝

  1. 修改 OtherOperation类
public class OtherOperation implements Cloneable{
    private String name;

    public OtherOperation clone() throws CloneNotSupportedException {
        System.out.println("复制OtherOperation对象!");
        return (OtherOperation) super.clone();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


  1. 修改WxPay,在clone中再复制OtherOperation ,以达到深拷贝的效果
public class WxPay implements Cloneable {

    private OtherOperation otherOperation = new OtherOperation();

    public WxPay() {
        System.out.println("创建支付对象");
    }

    public WxPay clone() throws CloneNotSupportedException {
        System.out.println("复制对象!");
        WxPay wxPay = (WxPay) super.clone();
        wxPay.otherOperation = otherOperation.clone();
        return wxPay;
    }

    public OtherOperation getOperation() {
        return otherOperation;
    }

    public String getName() {
        return otherOperation.getName();
    }

}
  1. 演示
public class demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        WxPay pay = new WxPay();
        WxPay pay1 = pay.clone();
        System.out.println(pay);
        System.out.println(pay1);
        System.out.println(pay.getOperation());
        System.out.println(pay1.getOperation());
        pay.getOperation().setName("abc");
        System.out.println(pay1.getOperation().getName());
    }
}

在这里插入图片描述

可以看到,WxPay 和 OtherOperation 都是新实例。

深拷贝实现方式 2:通过对象序列化实现深拷贝

  1. OtherOperation 实现 Serializable
public class OtherOperation implements  Serializable {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  1. WxPay 实现Serializable,并提供方法对当前对象进行序列化操作
public class WxPay implements Serializable {

    private OtherOperation otherOperation = new OtherOperation();

    public WxPay() {
        System.out.println("创建支付对象");
    }

    public OtherOperation getOperation() {
        return otherOperation;
    }

    public String getName() {
        return otherOperation.getName();
    }


    public WxPay deepClone() {
        //创建流对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this); //当前这个对象以对象流的方式输出
            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            return (WxPay) ois.readObject();
        }
        catch (Exception e) {
            return null;
        }
        finally {
            //关闭流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            }
            catch (Exception e2) {
                System.out.println(e2.getMessage());
            }
        }
    }
}
  1. 效果
public class demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        WxPay pay = new WxPay();
        WxPay pay1 = pay.deepClone();
        System.out.println(pay);
        System.out.println(pay1);
        System.out.println(pay.getOperation());
        System.out.println(pay1.getOperation());
        pay.getOperation().setName("abc");
        System.out.println(pay1.getOperation().getName());
    }
}

在这里插入图片描述

和上面我们手动的方式是一样的效果。

八、建造者模式

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

例如,在游戏场景中,需要构建人物模型,可能要先构建头部、再构建身体、然后四肢部分,最后才是一个完整的人物,如果都写在一起,肯定会导致代码复杂不易其他开发人员理解,对后期维护扩展难度都大,如果使用构建者模式,便可以将头部、身体、四肢,写在不同的人物实现里面,最后由一个构建类去按照一定的顺序加载它们,然后组成了一个更大的复杂的对象,这样便有利于维护扩展。

下面我们借助上面的例子简单用构建者模式实现下

  1. 定义人物对象,这里直接将头、身体、四肢简化为属性的形式表达。
@Data
public class Person {
    private String head;
    private String body;
    private String foot;
}
  1. 定义人物构建接口,并定义具体的抽象方法。
public interface PersonBuilder {
	//构建入口
	Person Builder();
	//构建头部
	void builderHead();
	//构建身体
	void builderBody();
	//构建四肢
	void builderFoot();
}
  1. 定义一个男孩人物的具体构建实现
public class BoyBuilder implements PersonBuilder {
    private Person person;

    public BoyBuilder() {
        person = new Person();
    }

    @Override
    public Person Builder() {
        builderHead();
        builderBody();
        builderFoot();
        System.out.println("构建完成!");
        return person;
    }

    @Override
    public void builderHead() {
        System.out.println("开始构建男孩头部...");
        person.setHead("男孩头部信息");
    }

    @Override
    public void builderBody() {
        System.out.println("开始构建男孩身体...");
        person.setBody("男孩身体信息");
    }

    @Override
    public void builderFoot() {
        System.out.println("开始构建男孩四肢...");
        person.setFoot("男孩四肢部分信息");
    }
    
}
  1. 演示
public class demo {

    public static void main(String[] args) {
        PersonBuilder personBuilder = new BoyBuilder();
        Person person = personBuilder.Builder();
        System.out.println(StringFormatter.concat("构建对象:", person.toString()).getValue());
    }
	
}

在这里插入图片描述

上面就是将一个人物的不同部分分别构建(实际中每个小的构建都应该在具体的实现中去表达,这里简化为了一个属性),一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

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

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

相关文章

TLSF算法概念,原理,内存碎片问题分析

TLSF算法介绍 TLSF(Two-Level Segregated Fit,两级分割适应算法)。 第一级(first level,简称fl):将内存大小按2的幂次方划分一个粗粒度的范围,如一个72字节的空闲内存的fl是6(72介…

Java画爱心

Java画爱心代码,每个人都可以被需要 效果图 源代码 package com.example.test; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import javax.swing.JFrame; class Cardioid extend…

具有标记和笔记功能的文件管理器TagSpaces(续)

熟悉老苏的读者都知道,老苏通常只是推荐软件,并简单介绍如何运行它们,而具体的功能则需要读者自行研究。这种方式让老苏能够在工作之余,还能保持每周发布 4 篇的更新。 然而,这种方式也存在明显的缺点。由于老苏没有深…

计算机基础知识65

cookie和session的使用 # 概念:cookie 是客户端浏览器上的键值对 # 目的:为了做会话保持 # 来源:服务端写入的,服务端再返回的响应头中写入,浏览器会自动取出来 存起来是以key value 形式,有过期时间、path…

深度学习在图像识别中的应用

深度学习在图像识别中的应用 摘要:本文介绍了深度学习在图像识别领域的应用,包括卷积神经网络(CNN)的基本原理、常见模型以及在图像识别中的优势。并通过实验展示了深度学习在图像识别中的实际应用和效果。 一、引言 随着数字化…

【踩坑记录】apex包的安装

你是不是也遇到了ModuleNotFoundError: No module named ‘packaging‘ error: subprocess-exited-with-error错误? apex介绍 此存储库包含nvidia维护的实用程序,以简化Pytorch中的混合精度和分布式训练。 这里的一些代码最终将包含在上游Pytorch中。 A…

冬季吃得过饱?羊大师教你几招消食的小妙招!

冬季吃得过饱?羊大师教你几招消食的小妙招! 冬季是人们容易吃得过饱的季节,美食诱惑频出,很容易导致胃口过大,饭量过多,造成消化不良甚至影响身体健康。所以,如何消食,让胃得到缓解…

CRC(循环冗余校验)直接计算和查表法

文章目录 CRC概述CRC名词解释宽度 (WIDTH)多项式 (POLY)初始值 (INIT)结果异或值 (XOROUT)输入数据反转(REFIN)输出数据反转(REFOUT) CRC手算过程模二加减&am…

【深度学习笔记】09 权重衰减

09 权重衰减 范数和权重衰减利用高维线性回归实现权重衰减初始化模型参数定义 L 2 L_2 L2​范数惩罚定义训练代码实现忽略正则化直接训练使用权重衰减 权重衰减的简洁实现 范数和权重衰减 在训练参数化机器学习模型时,权重衰减(decay weight&#xff09…

Chapter 6 Managing Application Engine Programs 管理应用程序引擎程序

Chapter 6 Managing Application Engine Programs 管理应用程序引擎程序 Running Application Engine Programs 运行应用程序引擎程序 This section provides an overview of program run options and discusses how to: 本节提供程序运行选项的概述,并讨论如何…

你知道SBTM(会话式测试管理)的最佳实践吗?

看到题目是不是感觉很陌生?没关系,今天一文讲透会话式测试管理。 一:什么是SBTM? SBTMSession-based test management会话式测试管理(简称SBTM)。它是一种灵活的测试方法,强调测试人员的自主性…

Vue学习计划--Vue2(二)Vue代理方式

Vue data中的两种方式 对象式 data:{}函数式 data(){return {} }示例&#xff1a; <body><div id"app">{{ name }} {{ age}} {{$options}}<input type"text" v-model"value"></div><script>let vm new Vue({el: …

Windows下搭建私有的MQTT服务器实现多设备间实时图传

一、前言 在Windows下使用EMQX搭建自己私有的MQTT服务器,实现多设备间实时图传效果。 测试了两种场景: 【1】图像采集端:采集电脑自己的摄像,通过MQTT协议上传到MQTT服务器,图像显示端订阅采集端的主题,获取实时图像显示。 【2】设备端:ESP32 + OV2640 采集图像上传到…

华为手环 8 五款免费表盘已上线,请注意查收

华为手环 8&#xff0c;作为一款集时尚与实用于一体的智能手环&#xff0c;不仅具备强大的功能&#xff0c;还经常更新的表盘样式&#xff0c;让用户掌控时间与健康的同时&#xff0c;也能展现自己的时尚品味。这不&#xff0c;12 月官方免费表盘又上新了&#xff0c;推出了五款…

【面试】Java最新面试题资深开发-JVM第一弹

问题一&#xff1a;Java中的垃圾回收机制 在Java中&#xff0c;垃圾回收是如何工作的&#xff0c;可以简要描述一下垃圾回收的算法有哪些吗&#xff1f; 在Java中&#xff0c;垃圾回收是一种自动管理内存的机制&#xff0c;它负责识别不再被程序引用的对象并释放其占用的内存…

常见的DOS命令、Java开发环境搭建、配置Path环境变量

目录 一、常见的DOS&#xff08;Disk Operating System、磁盘操作系统&#xff09;命令 二、Java开发环境搭建 1、什么是JDK、JRE 2、JDK版本选择 3、JDK的下载 三、配置Path环境变量 1、理解path环境变量 2、为什么配置path 3、如何配置 一、常见的DOS&#xff08;Dis…

实力出圈,开源网安连续4年入选中国网络安全企业100强

近日&#xff0c;安全牛第十一版《中国网络安全企业100强》正式发布。开源网安突出的综合实力、技术创新能力&#xff0c;以及前沿技术的落地应用成果&#xff0c;再次受到权威认可&#xff0c;从数百家安全厂商中脱颖而出&#xff0c;连续多年上榜百强榜单。 《中国网络安全企…

葡萄酒中的各种化合物都起着什么重要作用?

葡萄酒中的单宁和香兰素等酚类化合物是可以从橡木酒桶中陈酿中提取的。儿茶素是类黄酮&#xff0c;有助于构建各种单宁&#xff0c;并有助于感知葡萄酒中的苦味。它们在葡萄籽中浓度最高&#xff0c;但也存在于葡萄皮和茎中。儿茶素在葡萄浆果的微生物会在防御中发挥作用&#…

新生报到管理系统

【摘要】 随着我国教育水平的提高和新生数量的增加&#xff0c;合理妥善高效的进行新生接待&#xff0c;不但成为各个学校亟待解决的问题&#xff0c;对于广大新生来说&#xff0c;也是最先让他们了解学校形象的一个好机会。但是许多学校没有采用通过高效的系统来操作解决新生…

容器安全是什么

容器安全是当前面临的重要挑战之一&#xff0c;但通过采取有效的应对策略&#xff0c;我们可以有效地保护容器的安全。在应对容器安全挑战时&#xff0c;我们需要综合考虑镜像安全、网络安全和数据安全等多个方面&#xff0c;并采取相应的措施来确保容器的安全性。 德迅蜂巢原…