HeadFirest设计模式学习笔记

2023-05-16

设计原则

1、经常会发生变化的部分应该从整体中抽取并封装起来,以便以后可以很轻易的对这部分代码进行改动或者是扩充,而不会影响到不需要变化的其他部分。

2、针对接口编程,而不是针对实现编程。

3、多用组合,少用继承。组合:将适当的行为对象组合起来构成一个整体,善于利用接口编程,使组合建立的系统具有强大的弹性

4、为了交互对象之间的松耦合而努力(当两个对象之间松耦合,他们仍然可以交互,但是不清楚批次的细节)

5、类应该对修改关闭,对扩展开放

6、依赖倒置原则,要依赖抽象,不依赖具体实现

“高层”组件:由其他“低层”组件构成的类 例如:PizzaStore由不同的Pizza对象,准备、烘培、切片、装盒构成

“低层组件”:构成“高层”组件的构件 例如: 各种Pizza类型

“低层”与“高层”应该都依赖于抽象

所谓倒置:低层组件依赖高层的抽象。同样的,高层组件也依赖相同的抽象

工厂方法是依赖倒置的一种应用

7、最少知识原则:只和你的密友谈话

8、好莱坞原则:允许低层组件将自己挂载到高层组件上,但何时被调用以及如何被调用则由高层组件决定(模板方法模式的应用)

9、单一责任:一个类应该只有一个引起变化的原因

策略模式

定义:定义了算法族,分别封装起来,让它们之间可以相互替换,使得算法的变化独立于使用算法的客户

图解:下图有一组代表游戏角色的类和武器行为的类。每个角色一次只能使用一种武器,在游戏的过程中可以变换武器

在这里插入图片描述

WeaponBehavior:定义了使用武器的算法族

Character:使用WeaponBehavior算法族的类,其中的fight方法会调用weapon的useWeapon方法。Character类在运行的过程中可以动态改变useWeapon的行为,而useWeapon行为的变化不会影响到Character的结构

观察者模式

观察者模式就如同报纸的订阅。

1、报社的业务就是出版报纸

2、向某家报社订阅报纸,那么当报社出版新的报纸,就会就给送来。只要你是他们的订阅用户,就会一直受到新报纸

3、当你不想看报纸的时候,取消订阅,他们就不会再给你送新报纸了

4、只有报社还在运营就一直会有人订阅和取消

定义:观察者模式定义了对象之间一对多关系,这样一来,当一个对象的状态发生变化时,他的所有依赖者都会收到通知并自动更新

观察者模式有“推”和“拉”两种方式:

​ 推:主题的状态每次发生改变时,主动把所有信息通知给每一个观察者。

​ 拉:主题的状态每次发送改变时,观察者主动调用主题的getter来获取它们关心的数据。

​ 使用“拉”的好处:主题扩展功能时,不需要修改或更新每位观察者的调用,只需要添加对应数据的getter就可以

图解:下图有一个对象用于获取实时的天气情况,各个布告板对该对象进行监听,以便在天气状况发生变化时能及时的得到通知,更新显示数据

在这里插入图片描述

Subject:主题接口,对象通过此接口注册为观察者

Observer:当主题状态发生改变时调用update方法通知观察者对象

WeatherData:是一个监测天气数据的对象,当获取到最新的天气数据会通知给各个注册了的布告板

XXXDisplay:各个不同的布告板,注册监听WeatherData的消息,当收到新数据时更新显示内容

装饰者模式

装饰者模式是一个“对修改关闭,对扩展开放”的例子

利用继承扩展子类的行为,只能在编译时静态决定(行为不是来自超类,就是子类覆盖后的版本),而使用组合的方式扩展对象的行为可以在运行时动态的扩展对象行为

定义:装饰者模式可以动态的添加责任到对象上,若要扩展功能,装装饰者提供了比继承更加具有弹性的解决方案

例子:

1、一杯深焙咖啡

2、使用一个摩卡对象修饰它(现在,它是一杯摩卡深焙咖啡啦)

3、使用奶泡对象修饰它(现在,它是一杯奶泡摩卡深焙咖啡啦)

使用装饰者模式必须注意的地方:

1、装饰者对象和被装饰者对象必须是相同的类型,因为在任何需要原始对象的场合也可以使用修饰对象替换,就好比一杯深焙咖啡和一杯摩卡深焙咖啡,它们都是一杯咖啡。因此,可以使用修饰者和被修饰者具有相同的超类型来完成这一限定

2、可以用一个或多个修饰者对象包装一个对象

3、装饰者可以在被装饰者的行为之前或之后添加某些操作,来达到特定的目的

4、使用装饰者时程序中不能依赖具体的组件类型,因为当一个对象被装饰后,类型会发生变化

图解:下图有具体的饮料类和各种调味装饰类。由于装饰者与被装饰者必须具有相同的类型,所以它们都继承自共同的超类。

各个调味装饰类通过组合的方式对具体的饮料类进行扩展。例如:

​ new Mocha(new Mike(new DarkRoast())) // DarkRoast 深焙咖啡 被 Mike 牛奶 和 Mocat装饰,这是一杯牛奶摩卡深焙咖啡

在这里插入图片描述

Beverage:装饰类和被装饰类共同的超类

DarRoast、HouseBlend:饮料类的具体

CondimentDecorator:装饰类的抽象

Mike、Mocha、Soy:调味料的具体

简单工厂

简单工厂将创建对象的操作都封装到一个工厂类中,并对各个需要产品实例的客户程序都提供了一个接口获得产品对象。

客户程序通过工厂获取产品,它们无需关心产品的制作过程。日后当产品的制作过程发生变化,也不会对各个使用产品的客户产生影响。

在这里插入图片描述

public class SimpleFactory {    
    public Product createConcreteProduct(String str){        
        Product product = null ;        
        switch (str){            
            case "concrete1":product = new ConcreteProduct() ;break;            
            case "concrete2":product = new ConcreteProduct2();break;        
        }        
        return product ;    
    }
}
public class Product {}
public class ConcreteProduct extends Product {}
public class ConcreteProduct2 extends Product {}
public class Client {    
    private SimpleFactory simpleFactory ;    
    public Client(SimpleFactory simpleFactory){
    	this.simpleFactory = simpleFactory;
    }    
    public void method(){        
        //通过简单工厂来获取产品对象
        Product product = simpleFactory.createConcreteProduct("concrete1") ;  
        
        //do something with the product
    }
}

简单工厂的优点:将产品的“实现”与客户程序的“使用”分离开来,每当需要需要添加新产品或者产品的制作发生变化时,都不会影响到客户程序的使用。

工厂方法模式

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化哪个类

工厂方法让类把实例化推迟到子类

在这里插入图片描述

Factory类中的doSomething方法通常会包含依赖于抽象产品(Product)的代码,而这些抽象产品的实例由子类创建

Factory类无须了解产品创建的细节,当需要添加产品或者产品发生改变时,Factory类中引用抽象产品(Product)的代码将不会受到影响

public interface Product {}
public class ConcreteProduct1 implements Product {}
public class ConcreteProduct2 implements Product {}
public abstract class Factory {    
    public void doSomething(){        
        Product product = factoryMethod() ;        
        //do something whit the product    
        //以下使用product的代码,在添加产品或改变产品时将不会受到影响
    }    
    public abstract Product factoryMethod();
}
public class ConcreteFactory1 extends Factory {    
    @Override    //子类负责具体实现
    public Product factoryMethod() {        
        return new ConcreteProduct1() ;    
    }
}
public class ConcreteFactory2 extends Factory {    
    @Override    //子类负责具体实现
    public Product factoryMethod() {        
        return new ConcreteProduct2();    
    }
}

抽象工厂

定义:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类

抽象工厂允许客户程序使用抽象接口来创建一组相关联的产品,而不依赖于具体的产品对象

在这里插入图片描述

AbstractFactory:抽象工厂,提供了一个接口,用来创建一组相关联的产品

ConcreteFactoryA:具体工厂A,创建具体的产品

ConcreteFactoryB:具体工厂B,创建具体的产品

Client:客户程序,通过抽象工厂提供的接口来创建一组相关的产品,而不需要关心实际产出的产品是什么

public abstract class AbstractFactory {    
    public abstract AbstractProductA createProductA();    
    public abstract AbstractProductB createProductB() ;
}
public interface AbstractProductA { }
public interface AbstractProductB { }
public class Client {    
    private AbstractFactory abstractFactory  ;    
    public Client(AbstractFactory abstractFactory){        
        this.abstractFactory = abstractFactory ;    
    }    
    public void method(){        
        AbstractProductA productA = abstractFactory.createProductA();        
        AbstractProductB productB = abstractFactory.createProductB();        
        //do something whit the productA and the productB        
        //让客户端程序完全依赖抽象,而不是具体的产品    
    }    
    //测试    
    public static void main(String[] args) {  
        //要使用某个具体的子类工厂,必须先实例化它,然后将它传入到一些针对抽象工厂类型而写的代码中
        //使用A工厂创建产品        
        Client client1 = new Client(new ConcreteFactoryA()) ;        
        //使用B工厂创建产品        
        Client client2 = new Client(new ConcreteFactoryB()) ;    }
}
public class ConcreteFactoryA extends AbstractFactory {    
    @Override    
    public AbstractProductA createProductA() {        
        return new ProductA2();    
    }    
    @Override    
    public AbstractProductB createProductB() {        
        return new ProductB2() ;     
    }
}
public class ConcreteFactoryB extends AbstractFactory {    
    @Override    
    public AbstractProductA createProductA() {        
        return new ProductA1();    
    }    
    @Override    
    public AbstractProductB createProductB() {        
        return new ProductB1() ;    
    }
}
public class ProductA1 implements AbstractProductA { }
public class ProductA2 implements AbstractProductA {}
public class ProductB1 implements AbstractProductB {}
public class ProductB2 implements AbstractProductB {}

单例模式

定义:确保一个类只有一个实例,并且提供一个全局访问点

使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。

私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。

在这里插入图片描述

实现一、懒惰式-线程不安全

uniqueInstance被延迟到使用该类的时候才进行实例化,从而优化资源的浪费

以下实现是线程不安全的,当有多条线程执行到if(null == uniqueInstance)时,并且此时uniqueInstance 为null,则会有多条线执行到uniqueInstance = new Singleton(); 从而获取多个不一样的uniqueInstance对象

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

实现二、急切式-线程安全

JVM在加载这个类时马上创建唯一的单例对象,JVM保证在任何线程访问uniqueSingleton 静态变量前,一定先创建此实例

该实现方式丢失了延迟实例化节省资源的好处

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

实现三、懒惰式-线程安全

直接在getInstance方法添加添加(synchronized)同步锁,解决了实现一的线程安全问题,但是存在严重的性能问题。

只有在第一次实例化uniqueInstance对象,才真正需要用到同步锁,一旦uniqueInstance被实例化后,就不再需要同步这个操作了,以后每次调用getInstance方法,同步锁就是一个累赘(当一个线程进入该方法后,其他线程会被阻塞),影响执行效率。

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

实现四、双重检测加锁

将同步操作放在uniqueInstance实例化时进行,如果uniqueInstance已经被实例化则不再需要进行同步操作

public class Singleton {    
    //volatile保证uniqueInstance的有序性
    private static volatile Singleton uniqueInstance ;    
    private Singleton(){}    
    public static Singleton getIntance(){        
        if(null == uniqueInstance){            
            synchronized (Singleton.class){                
                if(null == uniqueInstance){                    
                    uniqueInstance = new Singleton() ;                 
                }            
            }        
        }        
        return  uniqueInstance ;    
    }
}

考虑下面的实现,也就是只是用一个if语句。在uniqueInstance == null的情况下,如果两个线程都执行了if语句

,并且两个线程都进入if语句块内。虽然在if语句块内进行了加锁操作,但是两个线程都会按顺序依次执行uniqueInstance = new Singleton();,从而创建出两个不同的uniqueInstance对象

使用双重判断:

第一个if避免uniqueInstance被实例化后还进行加锁操作

第二个if用来避免创建多个实例。由于第二个if是在同步代码块内,所以只有一个线程进入,不会出现 null == uniqueInstance 时,两个线程执行实例化操作

if (uniqueInstance == null) {
    synchronized (Singleton.class) {
        uniqueInstance = new Singleton();
    }
}

uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:

  1. 为 uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. 将 uniqueInstance 指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。

使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。

实现五、静态内部类

当 Singleton 类被加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance() 方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次(SingletonHolder在加载进内存时,在任何线程访问前,初始化静态实例域)。

这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。

public class Singleton {
    private Singleton() {}
    
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getUniqueInstance() {
        return SingletonHolder.INSTANCE;
    }
}

实现六、枚举实现

public enum Singleton {

    INSTANCE;

    private String objName;


    public String getObjName() {
        return objName;
    }


    public void setObjName(String objName) {
        this.objName = objName;
    }


    public static void main(String[] args) {

        // 单例测试
        Singleton firstSingleton = Singleton.INSTANCE;
        firstSingleton.setObjName("firstName");
        System.out.println(firstSingleton.getObjName());
        Singleton secondSingleton = Singleton.INSTANCE;
        secondSingleton.setObjName("secondName");
        System.out.println(firstSingleton.getObjName());
        System.out.println(secondSingleton.getObjName());

        // 反射获取实例测试
        try {
            Singleton[] enumConstants = Singleton.class.getEnumConstants();
            for (Singleton enumConstant : enumConstants) {
                System.out.println(enumConstant.getObjName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
firstName
secondName
secondName
secondName

该实现可以防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象,如果要防止这种攻击,需要在构造函数中添加防止多次实例化的代码。该实现是由 JVM 保证只会实例化一次,因此不会出现上述的反射攻击。

该实现在多次序列化和序列化之后,不会得到多个实例。而其它实现需要使用 transient 修饰所有字段,并且实现序列化和反序列化的方法。

命令模式

定义:命令模式将“请求”封装成对象,以便使用不同的请求队列或日志来参数化其他对象。命令模式也支持可撤销的操作。

Command:定义了所有命令对象的 “执行 ”和“撤销”操作的接口,调用这两个接口就可以让接收者执行相关操作

ConcreteCommand:绑定动作与接收者之间的关系

Receiver:动作的作用对象

Invoker:通过设置相关的命令对象执行相关的操作

Client:创建具体的命令对象并设置指定的接收者

在这里插入图片描述

public interface Command {    
    void execute() ;    
    void undo() ;
}
public class ConcreteCommand implements Command {    
    private Receiver receiver;    
    public ConcreteCommand(Receiver receiver){        
        this.receiver = receiver ;    
    }    
    @Override    
    public void execute() {        
        System.out.println("do something with the receiver");        
        receiver.on();    
    }    
    @Override    public void undo() {        
        System.out.println("undo something with the  receiver");        
        receiver.off();    }
}
public class Receiver {    
    public  void  on(){    }    
    public void off(){    }
}
public class Invoker {    
    private Command command ;    
    public void setCommand(Command command){        
        this.command = command ;    
    }    
    public void invoke(){        
        command.execute();    
    }
}
public class Client {    
    public static void main(String[] args) {        
        Invoker invoker = new Invoker() ;        
        Receiver receiver = new Receiver() ;        
        ConcreteCommand concreteCommand = new ConcreteCommand(receiver) ;        
        invoker.setCommand(concreteCommand);        
        invoker.invoke();    
    }
}

使用命令模式可以使调用者和接收者之间解耦

命令对象(Command)将动作和接收者包进对象中,对外提供execute方法。调用者不需要关心所拥有的是什么命令对象,只要该命令对象实现了Command接口即可

可以使用栈来记录多次命令从而使得撤销操作可以回到更早的状态

适配器

定义:将一个类的接口,转换成客户期望的另一个接口。适配让原本接口不兼容的类可以合作无间

在这里插入图片描述

客户端需要目标接口,使用适配器去包装被适配对象,使它能够满足客户端的要求

外观模式

外观模式的目标是简化接口,它将一个或数个类复杂的一切都隐藏在背后,只显露出一个干净的外观

定义:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个更高层次的接口,让子系统更容易使用

在这里插入图片描述

适配器更目标是“改变”,外观模式的目标是“简化”

模板方法模式

模板方法定义了一个算法的步骤,并且允许子类对一个或多个步骤提供实现

定义:在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法的结构的情况下,重新定义算法中的某个步骤

在这里插入图片描述

在这个抽象类中定义了templateMethod模板方法,该方法定义了一个算法的骨架,并且提供了两个抽象方法(这两个抽象方法分别是算法中的某一个步骤)让子类可以对算法的某个步骤进行改变。

public abstract class AbstractClass {    
    final void templateMethod(){        
        primitiveMethod1() ;        
        otherMethod();        
        primitiveMehtod2() ;        
        hook();    
    }    
    protected abstract void primitiveMethod1() ;    
    protected abstract void primitiveMehtod2() ;    
    private void otherMethod(){        
        System.out.println("do something in the AbstractClass");    
    }    
    public void hook(){        
        System.out.println("do something in the AbstractClass");    
    }
}
public class ConcreteClass extends AbstractClass {    
    @Override    
    protected void primitiveMethod1() {        
        System.out.println("do something in the ConcreteClass ");    
    }    
    @Override    
    protected void primitiveMehtod2() {        
        System.out.println("do something in the ConcreteClass ");    
    }    
    @Override    
    public void hook() {        
        System.out.println("do something in the ConcreteClass ");    
    }
}

迭代器模式

定义:提供了一种方法顺序访问一个聚合对象中的各个元素,而不暴露其内部的表示

可以把不同的元素放进数组、堆栈、队列、散列表等容器中,每一种容器都有其优点和使用的时机,假设现在你的客户想要遍历这个集合中的元素,他不应该去了解这些你是如何实现以及用何种方式去保存这些元素,因为这样做了的话,客户代码可能就会根据你的实现细节进行具体编码,当你存储元素的容器发生改变时,客户代码也不得不跟着改变。

针对具体编程

在这里插入图片描述

使用迭代器模式进行解耦

在这里插入图片描述

代理模式

定义:为另一个对象提供一个替身或占位符,以控制对这个对象的访问

代理对象本身并不提供真正的服务,而是通过调用目标对象的相关方法来提供相应的服务。

真正的业务功能还是由目标对象来完成,但是可以在业务功能执行的前后假如一些额外的功能

使用代理模式的目的

中介隔离作用:在某些情况下,不希望客户直接对目标对象进行访问,而代理对象正是客户与目标对象之间的中介

开闭原则,增强功能:可以通过代理类来扩展目标对象的功能,使用这种方式我们只需要修改代理对象而不需要修改目标对象,符合代码设计的开闭原则。可以让代理对象负责对消息的预处理然后在转发给目标对象,以及事后返回结果的处理。

静态代理

静态代理是由程序员或特定工具自动生成源代码,在对其进行编译。在程序运行之前,代理类.class文件就已经被创建了

要求代理对象与目标对象拥有相同的接口,使得代理类可以扮演目标类

客户程序可以把代理类认为是目标类

原理图:

在这里插入图片描述

Proxy和RealSubject都拥有相同的接口,使得Proxy可以扮演RealSubject的身份

RealSubject是真正做事情的对象

Proxy持有RealSubject的引用,以控制对RealSubject的访问

代码:

public interface Subject {    void request() ;}
public class RealSubject implements Subject {    
    @Override    
    public void request() {        
        System.out.println("提供服务");    
    }
}
public class Proxy implements Subject {    
    private Subject subject  ;    
    public Proxy(Subject subject){        
        this.subject = subject ;    
    }    
    @Override    
    public void request() {        
        System.out.println("提供服务前");        
        subject.request();        
        System.out.println("提供服务后");    
    }
}

静态代理的总结

优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展

缺点:我们得为每一个服务都创建代理类,工作量大,不易管理。

动态代理

在动态代理中我们不需要手动去创建代理对象,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态的创建

public interface Subject {    void request();}
public class RealSubject implements Subject {    
    public void request(){        
        System.out.println("处理数据");    
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler {    
    private Object object;    
                                                               
    public DynamicProxyHandler(Object object){        
        this.object = object ;    
     }    
    @Override    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
    {        
        System.out.println("处理前");        
        Object result = method.invoke(object, args);        
        System.out.println("处理后");        
        return result ;    
    }
}
public class Test {    
    public static void main(String[] args) {        
        RealSubject target = new RealSubject() ;        
        /*        * Proxy.newProxyInstance():        
        * ClassLoder loder:指定当前目标对象的类加载器        
        * Class<?>[] interfaces:指定目标对象实现的接口类型        
        * InvocationHandler:指定动态处理器,执行目标对象的方法时,会触
        * 发事件处理器的方法      
        * 
        */        
        Subject proxyTarget = 
            (Subject)Proxy.newProxyInstance(target.getClass().getClassLoader()   
                                            , new Class[]{Subject.class}   
                                            , new DynamicProxyHandler(target)); 
        //当调用代理对象的request方法时,会执行DynamicProxyHandler对象的invoke方法,并且把目标对象的request方法传递过去,这样就可以在invoke方法里面对目标对象的request方法进行增强
        proxyTarget.request();        
        System.out.println("2" + proxyTarget);    
    }
}

动态代理总结

虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。

CGLIB

静态代理与接口代理都需要目标对象实现一个接口,如果对于没有实现任何接口的目标类,我们也希望对其进行代理,这时候就拥戴CGLib代理。

CGLib代理也称为子类代理,它采用非常底层的字节码技术,通过分析目标类的字节码文件创建一个其子类,并在子类中采用方法拦截的技术,拦截所有父类方法的调用,顺势横切逻辑,但是因为CGLib代理采用的是继承,所以被代理类不能被final修饰。

JDK动态代理与CGLIB动态代理均是实现Spring AOP 的基础

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

HeadFirest设计模式学习笔记 的相关文章

  • ovn-architecture

    参考 文章目录 1 Name2 Description2 1 Information Flow in OVN OVN中的信息流向 2 2 Chassis Setup2 3 Logical Networks2 4 Life Cycle of
  • IDEA创建maven项目添加jar包依赖出错

    Problem1 xff1a 由于网络原因无法下载jar包 解决方法 xff1a 在maven的settings xml文件的 lt mirrors gt 标签中配置阿里镜像 lt mirror gt lt id gt nexus aliy
  • 判断一个字符串是否为回文字符串

    题目 输入一个字符串 xff0c 判断该字符串中除去空格之后的字符串是否为回文字符串 要求 xff1a 不可使用 Java 已实现的方法替换空格 xff0c 不可消耗额外的空间 代码实现 此处为判断方法的实现 public static b
  • Spring中的 JdbcTemplate和声明式事务控制

    Spring中的 JdbcTemplate和声明式事务控制 JdbcTemplate概述 JdbcTemplate的作用 xff1a 他就是用于和数据库交互的 xff0c 实现CRUD操作 如何创建该对象 在dao的实现类中定义并用set方
  • SpringMVC(一)

    SpringMVC xff08 一 xff09 SpringMVC的基本概念 三层架构 表现层业务层持久层 MVC模型 Model xff08 模型 xff09 xff1a 通常就是指我们的数据模型 xff0c 一般情况下用于封装数据 Vi
  • SpringMVC(二)

    SpringMVC xff08 二 xff09 响应数据和结果视图 返回值分类 xff1a 字符串voidModelAndView 对象 xff1a 是 spring 提供的一个对象 xff0c 可以用来调整具体的 JSP 视图 span
  • SpringMvc(三)

    SpringMvc xff08 三 xff09 SSM 整合可以使用多种方式 xff0c 一般会选择 XML 43 注解 的方式 整合的思路 xff1a 搭建整合环境先把spring 的配置搭建完成再使用 spring 整合SpringMV
  • 工厂方法模式(Factory Method)--多态工厂的实现

    工厂方法模式 xff08 Factory Method xff09 多态工厂的实现 定义 xff1a 定义一个用于创建对象的接口 xff0c 让子类决定实例化哪一个类 xff0c 工厂方法使一个类的实例化延迟到其子类 类图 xff1a 外链
  • 无人机自主定位导航避障VINS+fast_planner实测~

    厦大研一研究的一个项目 xff0c 将项目开发用到的技术和难点在这记录一下 常更新 xff0c 先把框架写好 xff0c 有空的时候就过来更新 xff0c 要是有漏的或者有错误的地方 xff0c 请大佬指点 因为采用的是TX2 xff0c
  • rs_D455相机内外参标定+imu联合标定

    IMU标定 lt launch gt lt node pkg 61 34 imu utils 34 type 61 34 imu an 34 name 61 34 imu an 34 output 61 34 screen 34 gt lt
  • GVINS论文简明解读

    VIN与GNSS融合的必要性 VIN系统只工作在局部坐标系下 x y z yaw不可观 里程计存在不可避免的漂移 而GNSS系统可提供无漂移的全局定位 VIN与GNSS融合的难点 不同于cmaera与imu此类的外参标定 GNSS坐标与VI
  • onos2.5.2编译安装

    onos编译安装 Ubuntu18 04 1 前置下载安装 1 1 前置包安装 参考docker file sudo apt get install y ca certificates zip python python3 git bzip
  • C++和C的区别(汇总)

    1 C是面向过程的语言 xff0c 而C 43 43 是面向对象的语言 2 C和C 43 43 动态管理内存的方法不一样 xff0c C是使用malloc free函数 xff0c 而C 43 43 除此之外还有new delete关键字
  • PX4学习笔记—通过串口发送自定义数据

    最近因为项目需要实现一个通过pixhawk串口收发自定义数据的功能 搜索发现 xff0c 博客上大神们FantasyJXF FreeApe的博文已经详细介绍了通过pixhawk串口读取自定义数据 xff0c 这部分功能实现后就可以将自己开发
  • 一步步入门搭建SpringSecurity OAuth2(密码模式)

    什么是OAuth2 xff1f 是开放授权的一个标准 xff0c 旨在让用户允许第三方应用去访问改用户在某服务器中的特定私有资源 xff0c 而可以不提供其在某服务器的账号密码给到第三方应用 大概意思就是比如如果我们的系统的资源是受保护的
  • STM32驱动SG90舵机与HC_SR04超声波模块

    前言 一 项目涉及的内容 项目简介 二 模块实操 1 SysTick系统定时器模块 2 SG90 舵机模块 3 HC SR04 超声波模块 4 main函数 总结 前言 这篇文章的内容主要对一个超声波 43 舵机小项目进行总结 xff0c
  • STM32基于IIC协议的OLED模块的使用

    前言 一 项目涉及的内容 项目简介 二 模块实操 1 IIC模块 1 1 IIC协议格式 1 2 开始信号与停止信号 1 3 写数据 1 3 1 硬件IIC代码编写 1 3 2 软件模拟IIC代码编写 2 OLED板块 前言 本篇文章对使用

随机推荐

  • STM32DMA功能详解

    目录 一 DMA的基本介绍 1 什么是DMA xff08 DMA的基本定义 xff09 2 DMA传输参数 3 DMA的主要特征 二 DMA功能框图 1 DMA请求 2 通道 3 仲裁器 三 DMA 数据配置 1 从哪里来到哪里去 外设到存
  • STM32对FreeRTOS单片机实时操作系统的移植

    前言 一 FreeRTOS是什么 二 FreeRTOS的移植 1 资料下载 2 开始移植 2 1 移植Source源码文件 2 2 添加 FreeRTOSConfig h 2 3 添加SYSTEM文件夹 2 4 复制 main c 文件进行
  • C语言 指针中的常见名称与用法

    目录 前言 一 指针是什么 二 指针与数组 数组指针 指针数组 三 指针与常量 指针常量 常量指针 四 指针与函数 指针函数 函数指针 前言 指针是C语言中大家接触的比较早但是也是内容比较多和实用的一块知识点 xff0c 之前虽然也大概知道
  • 过拟合及常见处理办法整理

    过拟合及常见处理办法整理 jingbo18的博客 CSDN博客 模型过拟合如何解决 判断方法 过拟合 xff08 over fitting xff09 xff0c 机器学习模型或者是深度学习模型在训练样本中表现得过于优越 xff0c 导致在
  • OBS 安装与考试参数设置及屏幕无法完全捕获、录屏不完整的解决方法

    目录 一 OBS 的下载与安装 二 OBS 考试参数设置 三 问题解决 xff08 1 xff09 屏幕无法完全捕获 xff08 2 xff09 录屏不完整 一 OBS 的下载与安装 官网 xff08 Open Broadcaster So
  • kube-proxy源码阅读(iptables实现)

    Reference 文章目录 1 入口2 ProxyServer创建及调用3 ProxyServer 核心调用流程3 1 func o Options Run err3 2 func o Options runLoop error3 3 f
  • TypeError: Expected Ptr<cv::UMat> for argument ‘image‘

    python3 43 opencv TypeError Expected Ptr xff1c cv UMat xff1e for argument image 输入的不是UMat格式 xff0c 使用cv2 UMat img get 转化一
  • SLAM中的因子图

    看论文 xff0c 发现很多SLAM方面的文章都涉及图优化 xff0c 其中更包含有因子图 正好前段时间看了PRML xff0c 将其进行整理 xff08 诶 xff0c 果然理论的内容就是得及时用起来 xff0c 现在又记不太清楚了 xf
  • TFmini Plus IIC在开源飞控 pixhawk上的应用

    TFmini Plus 可以在 Pixhawk 中使用 xff0c 以避开障碍物 本文结构 xff1a 1 TFmini Plus IIC在开源飞控 pixhawk上的应用 2 TFmini Plus参数设置 3 配置说明 1 TFmini
  • TFmini Plus 在开源飞控 pixhawk 上的应用

    TFmini Plus 在开源飞控 pixhawk 上的应用 TFmini Plus 可以直接连接 Pixhawk 的串口使用 飞行器可以使用 TFmini Plus 来实现定高或 者避障功能 本文档适用于 pixhawk ArduCopt
  • TFmini在开源飞控PX4上的应用

    TFmini在开源飞控PX4上的应用 TFmini 是一款小型激光雷达模组 主要实现实时 无接触式的距离测量功能 xff0c 具有测量准确 稳定 高速的特点 产品型号 xff1a TFmini 产品名称 xff1a 小型激光雷达模组 制造商
  • TFmini Plus在开源飞控PX4上的应用

    TFmini Plus在开源飞控PX4上的应用 PX4有着自己独特的优势 xff0c 受到广大爱好者的喜爱 TFmini Plus是北醒公司推出的性价比极高的激光雷达 xff0c 受到广大爱好者的追捧 本文介绍TFmini Plus和PX4
  • TFmini在开源飞控pixhawk上的应用

    TFmini在开源飞控pixhawk上的应用 TFmini可以直接连接Pixhawk的串口使用 飞行器可以使用TFmini来实现定高或者避障功能 本文档适用于pixhawk ArduCopter V3 6 2或更高版固件 xff08 注 x
  • 北邮oj-旋转图像

    include lt bits stdc 43 43 h gt using namespace std define maxn 105 int buf maxn maxn int ans maxn maxn int main int T N
  • 获取激光雷达数据

    从激光雷达获得距离 搭建turtlebot仿真环境 下载 sudo apt get install ros kinetic turtlebot 配置环境 sudo apt get install ros kinetic joy 将turtl
  • kube-proxy BoundedFrequencyRunner导致死循环分析

    kube proxy使用了k8s官方工具库中的BoundedFrequencyRunner实现基于事件及时间间隔的配置同步 1 BounderFrequencyRunner构建 1 1 相关核心代码 name Runner名称 func s
  • 捷联惯导系统学习2.6(圆锥误差补偿多子样算法)

    若圆锥运动的四元数更新方程为 xff1a Q t m 61 Q
  • 捷联惯导系统学习5.1(最小方差估计和线性最小方差估计)

    最小方差估计 最小均方误差MMSE 也称条件期望估计 使方差最小即 xff1a X 系 统 状 态 量
  • 13-Websocket协议与MQ协议

    Websocket协议 基于TCP全双工协议 xff0c 即可以从客户端向服务器端发送请求 xff0c 也可以从服务器端主动向客户端发送消息 HTTP协议只能从客户端向服务器发送请求 xff0c 服务器端收到请求后 xff0c 做出响应 x
  • HeadFirest设计模式学习笔记

    设计原则 1 经常会发生变化的部分应该从整体中抽取并封装起来 xff0c 以便以后可以很轻易的对这部分代码进行改动或者是扩充 xff0c 而不会影响到不需要变化的其他部分 2 针对接口编程 xff0c 而不是针对实现编程 3 多用组合 xf