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

六种常用设计模式

单例设计模式

单例模式指在整个系统生命周期里,保证一个类只能产生一个实例,确保该类的唯一性。

单例模式分类

单例模式可以分为懒汉式和饿汉式,两者之间的区别在于创建实例的时间不同:

  • 懒汉式:指系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。(这种方式要考虑线程安全)
  • 饿汉式:指系统一运行,就初始化创建实例,当需要时,直接调用即可。(本身就线程安全,没有多线程的问题)

单例类特点

  • 构造函数和析构函数为private类型,目的禁止外部构造和析构
  • 拷贝构造和赋值运算符重载函数为private类型,目的是禁止外部拷贝和赋值,确保实例的唯一性

单例类的结构

  • 一个public的获取指向唯一的实例对象的指针的函数GetInstance()
  • 构造函数析构函数设为private,禁止外部构造和析构
  • 拷贝构造和赋值操作符重载函数设为private类型,禁止外部拷贝和赋值,确保实例的唯一性
  • 一个private的static的指向唯一的实例对象的指针

代码实现

//线程安全的懒汉模式
class singleClass {
public:static singleClass* getinstance(){//双重锁模式if (instance == nullptr){//先判断是否为空,如果为空则进入,不为空说明已经存在实例,直接返回//进入后加锁i_mutex.lock();if (instance == nullptr){//再判断一次,确保不会因为加锁期间多个线程同时进入instance = new singleClass();}i_mutex.unlock();//解锁}return instance;}
private:static singleClass* instance;static mutex i_mutex;//锁singleClass(){}singleClass(const singleClass& sc) {}//拷贝构造函数也需要设置为私有
};
singleClass* singleClass::instance=nullptr;
mutex singleClass::i_mutex;//类外初始化
//饿汉模式:不管用不用得到,都构造出来。本身就是线程安全的
class ehsingleClass {
public:static ehsingleClass* getinstance(){return instance;}private:static ehsingleClass* instance;//静态成员变量必须类外初始化,只有一个ehsingleClass() {};ehsingleClass(const ehsingleClass& sc) {}//拷贝构造函数也需要设置为私有
};
ehsingleClass* ehsingleClass::instance = new ehsingleClass();
//类外定义,main开始执行前,该对象就存在了

工厂设计模式

工厂模式概念

工厂模式是一种创建型设计模式,它提供了一种封装对象创建过程的方式,从而使得客户端代码与创建具体对象的过程解耦合。

工厂模式主要包含以下几种变体:

  1. 简单工厂模式(Simple Factory Pattern):简单工厂模式通过一个工厂类来创建对象,客户端通过调用工厂类的静态方法或非静态方法来获取所需的对象实例。这种模式不符合开闭原则,因为每次新增产品都需要修改工厂类。

  2. 工厂方法模式(Factory Method Pattern):工厂方法模式将对象的创建委托给子类来完成。定义一个创建对象的接口,但让子类决定实例化哪个类。这种模式遵循了开闭原则,因为可以通过添加新的子类来扩展系统功能。

  3. 抽象工厂模式(Abstract Factory Pattern):抽象工厂模式提供了一个接口,用于创建相关或依赖对象的家族,而不需要指定具体的类。它是工厂方法模式的扩展,通过定义多个工厂接口来创建一系列相关对象。

工厂模式的核心思想是将对象的创建过程封装起来,使得客户端代码不需要直接依赖于具体对象的创建过程,而是依赖于工厂接口或方法来获取所需的对象实例。这样可以降低代码的耦合度,提高系统的灵活性和可维护性。

工厂模式适用于以下情况:

  • 当一个类不知道它必须创建的对象的类时。
  • 当一个类希望由其子类来指定所创建的对象时。
  • 当需要将对象的创建和使用分离时,以便更好地组织代码结构和逻辑关系。

1. 简单工厂模式(Simple Factory Pattern)

#include <iostream>// 抽象产品类
class Product {
public:virtual void operation() = 0;virtual ~Product() {}
};// 具体产品类A
class ConcreteProductA : public Product {
public:void operation() override {std::cout << "ConcreteProductA: operation()" << std::endl;}
};// 具体产品类B
class ConcreteProductB : public Product {
public:void operation() override {std::cout << "ConcreteProductB: operation()" << std::endl;}
};// 简单工厂类
class SimpleFactory {
public:static Product* createProduct(char type) {switch (type) {case 'A':return new ConcreteProductA();case 'B':return new ConcreteProductB();default:return nullptr;}}
};int main() {// 创建具体产品对象Product* productA = SimpleFactory::createProduct('A');Product* productB = SimpleFactory::createProduct('B');// 调用具体产品对象的方法if (productA) productA->operation();if (productB) productB->operation();// 释放资源delete productA;delete productB;return 0;
}

2. 工厂方法模式(Factory Method Pattern)

#include <iostream>// 抽象产品类
class Product {
public:virtual void operation() = 0;virtual ~Product() {}
};// 具体产品类A
class ConcreteProductA : public Product {
public:void operation() override {std::cout << "ConcreteProductA: operation()" << std::endl;}
};// 具体产品类B
class ConcreteProductB : public Product {
public:void operation() override {std::cout << "ConcreteProductB: operation()" << std::endl;}
};// 抽象工厂类
class Factory {
public:virtual Product* createProduct() = 0;virtual ~Factory() {}
};// 具体工厂类A
class ConcreteFactoryA : public Factory {
public:Product* createProduct() override {return new ConcreteProductA();}
};// 具体工厂类B
class ConcreteFactoryB : public Factory {
public:Product* createProduct() override {return new ConcreteProductB();}
};int main() {// 创建具体工厂对象Factory* factoryA = new ConcreteFactoryA();Factory* factoryB = new ConcreteFactoryB();// 创建具体产品对象Product* productA = factoryA->createProduct();Product* productB = factoryB->createProduct();// 调用具体产品对象的方法if (productA) productA->operation();if (productB) productB->operation();// 释放资源delete factoryA;delete factoryB;delete productA;delete productB;return 0;
}

3. 抽象工厂模式(Abstract Factory Pattern)

#include <iostream>// 抽象产品类A
class AbstractProductA {
public:virtual void operationA() = 0;virtual ~AbstractProductA() {}
};// 具体产品类A1
class ConcreteProductA1 : public AbstractProductA {
public:void operationA() override {std::cout << "ConcreteProductA1: operationA()" << std::endl;}
};// 具体产品类A2
class ConcreteProductA2 : public AbstractProductA {
public:void operationA() override {std::cout << "ConcreteProductA2: operationA()" << std::endl;}
};// 抽象产品类B
class AbstractProductB {
public:virtual void operationB() = 0;virtual ~AbstractProductB() {}
};// 具体产品类B1
class ConcreteProductB1 : public AbstractProductB {
public:void operationB() override {std::cout << "ConcreteProductB1: operationB()" << std::endl;}
};// 具体产品类B2
class ConcreteProductB2 : public AbstractProductB {
public:void operationB() override {std::cout << "ConcreteProductB2: operationB()" << std::endl;}
};// 抽象工厂类
class AbstractFactory {
public:virtual AbstractProductA* createProductA() = 0;virtual AbstractProductB* createProductB() = 0;virtual ~AbstractFactory() {}
};// 具体工厂类1
class ConcreteFactory1 : public AbstractFactory {
public:AbstractProductA* createProductA() override {return new ConcreteProductA1();}AbstractProductB* createProductB() override {return new ConcreteProductB1();}
};// 具体工厂类2
class ConcreteFactory2 : public AbstractFactory {
public:AbstractProductA* createProductA() override {return new ConcreteProductA2();}AbstractProductB* createProductB() override {return new ConcreteProductB2();}
};int main() {// 创建具体工厂对象AbstractFactory* factory1 = new ConcreteFactory1();AbstractFactory* factory2 = new ConcreteFactory2();// 创建具体产品对象AbstractProductA* productA1 = factory1->createProductA();AbstractProductB* productB1 = factory1->createProductB();AbstractProductA* productA2 = factory2->createProductA();AbstractProductB* productB2 = factory2->createProductB();// 调用具体产品对象的方法if (productA1) productA1->operationA();if (productB1) productB1->operationB();if (productA2) productA2->operationA();if (productB2) productB2->operationB();// 释放资源delete factory1;delete factory2;delete productA1;delete productB1;delete productA2;delete productB2;return 0;
}

抽象工厂模式(Abstract Factory Pattern)和工厂方法模式(Factory Method Pattern)都属于工厂模式的范畴,但它们之间有一些关键区别:

  1. 目的

    • 抽象工厂模式旨在提供一个接口,用于创建一系列相关或依赖对象的家族,而不需要指定具体的类。
    • 工厂方法模式旨在将对象的创建委托给子类来完成。它定义一个创建对象的接口,但让子类决定实例化哪个类。
  2. 结构

    • 抽象工厂模式通常由一个抽象工厂接口和多个具体工厂类组成。每个具体工厂类负责创建一个产品家族的产品。
    • 工厂方法模式通常由一个抽象产品类和一个抽象工厂类组成。抽象工厂类定义了创建产品的方法,具体工厂类负责实现这些方法来创建具体产品。
  3. 扩展

    • 抽象工厂模式通过添加新的具体工厂类来扩展系统,每个具体工厂类负责创建一个产品家族的产品。
    • 工厂方法模式通过添加新的具体工厂类或扩展现有的抽象工厂类来扩展系统,每个具体工厂类负责创建一个具体产品。
  4. 关系

    • 抽象工厂模式通常与工厂方法模式结合使用,一个抽象工厂类中可以包含多个工厂方法,每个工厂方法负责创建一个具体产品。
    • 工厂方法模式是抽象工厂模式的一个特例,它将抽象工厂类中的工厂方法设计成抽象的,然后由具体子类来实现。

观察者模式

观察者模式(Observer Pattern)

观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象监听一个主题对象,当主题对象的状态发生变化时,所有依赖于它的观察者都会收到通知并自动更新。

在观察者模式中,有三个关键角色:

  1. Subject(主题):也称为被观察者或可观察对象,它是一个抽象类或接口,定义了被观察者需要实现的方法。主题对象维护一个观察者列表,并提供方法来注册、移除和通知观察者。

  2. Observer(观察者):也称为订阅者或监听者,它是一个抽象类或接口,定义了观察者需要实现的方法。观察者对象通过订阅主题对象来接收状态变化的通知,并执行相应的操作。

  3. ConcreteSubject(具体主题):是主题对象的具体实现类,它继承或实现了主题接口,并维护了一个状态变量。当状态变量发生变化时,具体主题对象会通知所有注册的观察者。

  4. ConcreteObserver(具体观察者):是观察者对象的具体实现类,它继承或实现了观察者接口,并定义了在接收到主题对象通知时所需要执行的操作。

观察者模式的优点包括:

  • 松耦合:主题对象和观察者对象之间是松耦合的,它们之间没有直接的依赖关系,可以独立地进行修改和扩展。
  • 可重用性:可以在不同的主题和观察者之间进行重复使用,使得代码更加灵活和可维护。
  • 多态性:可以通过继承和接口实现多态性,从而可以根据需要定义不同类型的主题和观察者。

观察者模式适用于以下情况:

  • 当一个对象的状态变化需要通知其他对象,并且不知道有多少个对象需要通知时。
  • 当一个对象的状态变化需要通知其他对象,但不希望这些对象与之耦合时。
  • 当一个对象的状态变化会导致其他对象的行为发生变化时。

工作流程:

观察者模式的工作流程通常涉及以下几个步骤:

  1. 定义主题接口:首先,需要定义一个主题接口或抽象类,其中包含了注册、移除和通知观察者的方法。这个接口或抽象类定义了主题对象的基本行为。

  2. 定义观察者接口:然后,需要定义一个观察者接口或抽象类,其中包含了接收通知并进行相应操作的方法。这个接口或抽象类定义了观察者的基本行为。

  3. 创建具体主题类:接着,创建一个具体主题类,实现主题接口,并维护一个观察者列表。具体主题类通常包含一个状态变量,当状态变化时会通知所有注册的观察者。

  4. 创建具体观察者类:然后,创建一个或多个具体观察者类,实现观察者接口,并定义在接收到主题对象通知时所需要执行的操作。每个具体观察者类通常包含一个指向具体主题对象的引用。

  5. 注册观察者:在需要订阅主题对象的观察者处,将具体观察者对象注册到具体主题对象的观察者列表中。

  6. 状态变化通知:当具体主题对象的状态发生变化时,调用通知方法,遍历观察者列表,并依次通知每个观察者对象。

  7. 观察者响应:每个观察者对象在接收到通知后,会执行相应的操作,根据具体业务需求进行处理。

  8. 取消注册观察者(可选):如果观察者不再对主题对象的状态变化感兴趣,可以取消注册观察者,将其从观察者列表中移除

代码实现

#include <iostream>
#include <vector>// 定义观察者接口
class Observer {
public:virtual void update(int data) = 0;virtual ~Observer() {}
};// 定义具体观察者类
class ConcreteObserver : public Observer {
public:void update(int data) override {std::cout << "ConcreteObserver received update: " << data << std::endl;}
};// 定义主题接口
class Subject {
public:virtual void attach(Observer* observer) = 0;virtual void detach(Observer* observer) = 0;virtual void notify(int data) = 0;virtual ~Subject() {}
};// 定义具体主题类
class ConcreteSubject : public Subject {
public:void attach(Observer* observer) override {observers.push_back(observer);}void detach(Observer* observer) override {auto it = std::find(observers.begin(), observers.end(), observer);if (it != observers.end()) {observers.erase(it);}}void notify(int data) override {for (Observer* observer : observers) {observer->update(data);}}private:std::vector<Observer*> observers;
};int main() {// 创建具体主题对象ConcreteSubject subject;// 创建具体观察者对象ConcreteObserver observer1;ConcreteObserver observer2;// 注册观察者subject.attach(&observer1);subject.attach(&observer2);// 发送通知subject.notify(123);// 移除观察者subject.detach(&observer1);// 再次发送通知subject.notify(456);return 0;
}

代理模式

代理模式(Proxy Pattern)

代理模式(Proxy Pattern)是一种结构型设计模式,它允许你提供一个代理对象来控制对其他对象的访问。代理对象通常充当客户端和目标对象之间的中间人,客户端通过代理对象间接访问目标对象,从而可以在访问过程中添加额外的功能,如延迟加载、访问控制、缓存等。

在代理模式中,通常有三个角色:

  1. Subject(抽象主题):定义了目标对象和代理对象共同实现的接口,它可以是抽象类或接口。抽象主题可以是真实主题和代理对象的共同接口,也可以是真实主题的接口。

  2. RealSubject(真实主题):定义了真实对象的具体实现,是代理模式中的核心对象。客户端最终想要访问的对象就是真实主题。

  3. Proxy(代理):实现了抽象主题接口,并维护一个指向真实主题的引用。代理对象在执行目标对象方法的同时,可以在方法执行前后添加额外的逻辑。

代理模式的优点包括:

  • 控制访问:可以通过代理对象来控制对真实对象的访问,如权限控制、审计等。
  • 增加功能:可以在不改变目标对象的前提下,通过代理对象为目标对象增加额外的功能,如缓存、延迟加载、日志记录等。
  • 保护目标对象:可以对真实对象进行保护,防止客户端直接访问,从而提高系统的安全性。

代理模式适用于以下情况:

  • 当需要在访问一个对象时添加额外的功能,但又不想修改原有的代码时。
  • 当需要对访问进行控制和保护时,如权限控制、审计等。
  • 当需要在访问一个远程对象或昂贵对象时进行性能优化,如延迟加载、缓存等。

代码实现

#include <iostream>// 定义抽象主题接口
class Subject {
public:virtual void request() = 0;virtual ~Subject() {}
};// 定义具体主题类
class RealSubject : public Subject {
public:void request() override {std::cout << "RealSubject: Handling request." << std::endl;}
};// 定义代理类
class Proxy : public Subject {
public:Proxy(Subject* realSubject) : realSubject(realSubject) {}void request() override {if (checkAccess()) {realSubject->request();logAccess();} else {std::cout << "Proxy: Access denied." << std::endl;}}private:Subject* realSubject;bool checkAccess() {// 检查访问权限的逻辑std::cout << "Proxy: Checking access..." << std::endl;return true; // 简单起见,这里直接返回 true}void logAccess() {// 记录访问日志的逻辑std::cout << "Proxy: Logging access..." << std::endl;}
};int main() {// 创建真实主题对象RealSubject* realSubject = new RealSubject();// 创建代理对象,并将真实主题对象传入代理对象的构造函数中Proxy* proxy = new Proxy(realSubject);// 通过代理对象访问真实主题对象的方法proxy->request();// 释放资源delete proxy;delete realSubject;return 0;
}

装饰器模式

装饰器模式(Decorator Pattern)

装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许你通过将对象放入包装器中来动态地扩展其行为。装饰器模式提供了一种灵活的方式来添加功能,而无需修改现有的代码。

在装饰器模式中,有四个关键角色:

  1. Component(组件):是一个抽象类或者接口,定义了被装饰对象和装饰器共同遵循的协议或契约。它可以是抽象类或接口,定义了具体组件和装饰器必须实现的操作。

  2. Concrete Component(具体组件):是实现了组件接口的具体对象。具体组件是被装饰的对象,它定义了基本行为,可以通过装饰器来扩展。

  3. Decorator(装饰器):是一个抽象类,实现了组件接口,并包含一个对组件对象的引用。装饰器可以根据需要对组件对象进行包装,以扩展其行为。

  4. Concrete Decorator(具体装饰器):是实现了装饰器接口的具体对象。具体装饰器包装了具体组件对象,并在其基础上添加额外的行为或功能。

装饰器模式的工作流程:

  1. 定义组件接口:首先,定义一个组件接口或抽象类,它声明了被装饰对象和装饰器共同遵循的协议或契约。组件接口通常包含一个或多个方法,用于定义组件的基本行为。

  2. 实现具体组件类:根据组件接口,创建一个具体的组件类,实现组件接口中定义的方法。具体组件类是被装饰的对象,它定义了基本行为,是装饰器模式的核心。

  3. 创建装饰器抽象类:定义一个装饰器抽象类,它实现了组件接口,并包含一个对组件对象的引用。装饰器抽象类提供了一个统一的接口,用于包装组件对象并在其基础上添加额外的功能。

  4. 实现具体装饰器类:根据装饰器抽象类,创建具体的装饰器类,实现装饰器抽象类中定义的方法。具体装饰器类可以根据需要在组件对象的基础上添加额外的行为或功能,从而扩展组件的功能。

  5. 创建装饰器链:根据业务需求,可以将多个装饰器对象按照一定的顺序组合成一个装饰器链。装饰器链中的每个装饰器对象都包装了一个组件对象,并可以在其基础上添加额外的功能。

  6. 使用装饰器模式:在客户端代码中,根据需要创建具体组件对象,并根据业务需求创建相应的装饰器对象,并将其按照一定的顺序组合成装饰器链。然后通过调用装饰器链的方法来执行操作,装饰器链会根据其包装的组件对象的类型以及装饰器对象的顺序依次执行相应的功能。

代码实现:

#include <iostream>// 抽象组件
class Component {
public:virtual void operation() = 0;virtual ~Component() {}
};// 具体组件
class ConcreteComponent : public Component {
public:void operation() override {std::cout << "ConcreteComponent: operation()" << std::endl;}
};// 装饰器抽象类
class Decorator {
public:Decorator(Component* component) : component(component) {}void operation()  {if (component != nullptr) {component->operation();}}protected:Component* component;
};// 具体装饰器A
class ConcreteDecoratorA : public Decorator {
public:ConcreteDecoratorA(Component* component) : Decorator(component) {}void operation() override {Decorator::operation();addBehavior();}void addBehavior() {std::cout << "ConcreteDecoratorA: addBehavior()" << std::endl;}
};// 具体装饰器B
class ConcreteDecoratorB : public Decorator {
public:ConcreteDecoratorB(Component* component) : Decorator(component) {}void operation() override {Decorator::operation();addBehavior();}void addBehavior() {std::cout << "ConcreteDecoratorB: addBehavior()" << std::endl;}
};int main() {// 创建具体组件对象Component* component = new ConcreteComponent();// 创建具体装饰器A,并包装具体组件对象Decorator* decoratorA = new ConcreteDecoratorA(component);// 创建具体装饰器B,并包装具体装饰器ADecorator* decoratorB = new ConcreteDecoratorB(decoratorA);// 执行操作decoratorB->operation();// 释放资源delete decoratorB;delete decoratorA;delete component;return 0;
}

策略模式

策略模式(Strategy Pattern)

策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列算法,并使得这些算法可以相互替换,使得客户端在使用算法时可以独立于其具体实现。策略模式将算法的定义、使用和实现分离开来,使得每个算法可以独立变化而不影响其他部分。

在策略模式中,有三个关键角色:

  1. Context(上下文):上下文是策略模式的核心类,它包含了一个策略接口的引用,并在运行时根据具体的情况选择合适的算法来执行。上下文类通常提供一个设置策略的方法,以及一个执行策略的方法。

  2. Strategy(策略):策略是一个接口或抽象类,它定义了一系列算法的共同接口。具体的策略类实现了策略接口,并提供了具体的算法实现。

  3. ConcreteStrategy(具体策略):具体策略是策略模式的具体实现类,它实现了策略接口,并提供了具体的算法实现。

策略模式的优点包括:

  • 分离算法:将算法的定义、使用和实现分离开来,使得每个算法可以独立变化而不影响其他部分。
  • 扩展性:增加新的算法非常方便,只需要实现新的策略类,并在上下文中设置即可。
  • 复用性:策略模式使得算法可以在不同的上下文中共享和重复使用。

策略模式适用于以下情况:

  • 当一个类有多种行为,且需要在运行时根据具体情况选择合适的行为时。
  • 当一个类的行为可以通过组合不同的算法来实现,并且这些算法可以相互替换时。
  • 当一个类的行为在不同的上下文中需要不同的实现时。

工作流程

策略模式的工作流程可以描述为以下几个步骤:

  1. 定义策略接口(Strategy Interface):首先,定义一个策略接口,该接口包含了需要实现的算法方法。这个接口可以是一个抽象类或者一个纯虚函数接口,具体取决于编程语言和具体需求。

  2. 实现具体策略类(Concrete Strategy Classes):接下来,为每个具体的算法实现类编写具体的策略类。这些类实现了策略接口,并提供了具体的算法逻辑。

  3. 创建上下文类(Context Class):定义一个上下文类,它包含了一个指向策略接口的引用。上下文类负责在运行时根据具体的情况选择合适的策略,并将任务委托给策略对象。

  4. 设置和切换策略:在上下文类中提供设置和切换策略的方法,使得客户端可以根据需要选择不同的策略。

  5. 客户端调用:客户端创建上下文对象,并根据具体的需求选择合适的策略,并调用相应的方法执行算法。

代码实现

#include <iostream>// 定义策略接口
class Strategy {
public:virtual void execute() = 0;virtual ~Strategy() {}
};// 实现具体策略类
class ConcreteStrategyA : public Strategy {
public:void execute() override {std::cout << "Executing strategy A" << std::endl;}
};class ConcreteStrategyB : public Strategy {
public:void execute() override {std::cout << "Executing strategy B" << std::endl;}
};// 创建上下文类
class Context {
public:Context(Strategy* strategy) : strategy(strategy) {}// 设置和切换策略void setStrategy(Strategy* newStrategy) {strategy = newStrategy;}// 调用策略方法void executeStrategy() {strategy->execute();}private:Strategy* strategy;
};int main() {// 创建具体策略对象ConcreteStrategyA strategyA;ConcreteStrategyB strategyB;// 创建上下文对象,并设置初始策略Context context(&strategyA);// 执行初始策略context.executeStrategy(); // 输出 "Executing strategy A"// 切换策略并执行context.setStrategy(&strategyB);context.executeStrategy(); // 输出 "Executing strategy B"return 0;
}

在这个示例中,Strategy 是策略接口,定义了执行策略的方法。ConcreteStrategyAConcreteStrategyB 是具体策略类,分别实现了策略接口中的方法,即具体的算法实现。Context 是上下文类,它包含一个策略接口的引用,并提供了设置策略和执行策略的方法。在 main 函数中,创建了具体策略对象和上下文对象,并使用上下文对象执行了具体的策略。

相关文章:

  • QT状态机10-QKeyEventTransition和QMouseEventTransition的使用
  • 海外仓储管理系统:提升效率,标准化海外仓管理,科技赋能业务
  • 学习Uni-app开发小程序Day17
  • 前端请求超时截断,axios timeout设置未生效情况记录
  • k8s笔记 | helm包管理
  • Spring 事务源码分析
  • Docker配置国内镜像源
  • 匿名内部类(重点)
  • Linux磁盘高级操作
  • 计算机网络数据链路层知识点总结
  • OpenHarmony集成OCR三方库实现文字提取
  • golang中的位运算 << >> ^ 高位数,低位数示例
  • 有趣的css - 两个圆形加载效果
  • MVS net笔记和理解
  • JAVA面试题大全(九)
  • [LeetCode] Wiggle Sort
  • “大数据应用场景”之隔壁老王(连载四)
  • 08.Android之View事件问题
  • 4个实用的微服务测试策略
  • Babel配置的不完全指南
  • HashMap ConcurrentHashMap
  • REST架构的思考
  • Spark RDD学习: aggregate函数
  • 翻译:Hystrix - How To Use
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 前嗅ForeSpider中数据浏览界面介绍
  • 区块链共识机制优缺点对比都是什么
  • 算法之不定期更新(一)(2018-04-12)
  • 王永庆:技术创新改变教育未来
  • 小试R空间处理新库sf
  • 阿里云移动端播放器高级功能介绍
  • 如何在招聘中考核.NET架构师
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​【已解决】npm install​卡主不动的情况
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • ​渐进式Web应用PWA的未来
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • $Django python中使用redis, django中使用(封装了),redis开启事务(管道)
  • (2022 CVPR) Unbiased Teacher v2
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (分享)一个图片添加水印的小demo的页面,可自定义样式
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • (七)c52学习之旅-中断
  • (三)模仿学习-Action数据的模仿
  • (十五)使用Nexus创建Maven私服
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (一)utf8mb4_general_ci 和 utf8mb4_unicode_ci 适用排序和比较规则场景
  • ******之网络***——物理***
  • ****三次握手和四次挥手
  • .bashrc在哪里,alias妙用
  • .NET Core Web APi类库如何内嵌运行?
  • .Net Core缓存组件(MemoryCache)源码解析
  • .NET Reactor简单使用教程
  • .NetCore实践篇:分布式监控Zipkin持久化之殇