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

同步异步日志系统:设计模式

   

    设计模式是前辈们对代码开发经验的总结,是解决特定问题的⼀系列套路。它不是语法规定,⽽是⼀ 套⽤来提⾼代码可复⽤性、可维护性、可读性、稳健性以及安全性的解决⽅案。

         为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打 仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后 来孙子就总结出了《孙子兵法》。孙子兵法也是类似。

       使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。 

一、六大原则

1.1 单一职责原则

单⼀职责原则(Single Responsibility Principle):类的职责应该单⼀,⼀个⽅法只做⼀件事。职责划分清晰了,每次改动到最⼩单位的⽅法或类。

使⽤建议:两个完全不一样的功能不应该放⼀个类中,⼀个类中应该是⼀组相关性很⾼的函 数、数据的封装  

用例:网络聊天:⽹络通信&聊天,应该分割成为网络通信类&聊天类 

这样如果我们不用网络通信了,就不需要修改聊天相关的代码

1.2  开闭原则

开闭原则(Open Closed Principle):对扩展开放,对修改封闭

使用建议:对软件实体的改动,最好用扩展而非修改的方式。

用例:超时卖货:商品价格---不是修改商品的原来价格,而是新增促销价格。

       临期过期商品需要低价处理的时候,不是修改原价格,而是新增一个促销价格,这样后期的时候可以直接去掉促销价格而不需要去修改原价格

1.3 里氏替换原则

⾥⽒替换原则(Liskov Substitution Principle):通俗点讲,就是只要⽗类能出现的地方,子类就可以出现,而且替换为子类也不会产生任何错误或异常

动机:它鼓励我们在设计类时,应该关注类的抽象,而不是具体的实现,这样当我们需要对类进行扩展时,就可以通过创建新的子类来完成,而不是直接修改基类。这种设计方式有助于减少代码的耦合度,提高代码的可维护性和可扩展性。 

      动机的背后是面向对象设计中的一个基本矛盾:一方面,我们希望类的功能是封闭的,即一个类应该只关注自己的业务逻辑,而不关心其他类的细节;另一方面,我们希望类的功能是可扩展的,即在不修改原有代码的情况下,能够方便地对类进行扩展。里氏代换原则正是为了解决这个矛盾而提出的。在继承类时,务必重写⽗类中所有的⽅法,尤其需要注意⽗类的protected⽅法,⼦类尽量不要 暴露⾃⼰的public⽅法供外界调⽤。

使用建议:子类必须完全实现父类的⽅法,孩⼦类可以有自己的个性。覆盖或实现⽗类的⽅法 时,输⼊参数可以被放大,输出可以缩小

用例:跑步运动员类-会跑步,⼦类⻓跑运动员-会跑步且擅长长跑,子类短跑运动员-会跑步且擅长短跑

1.4 依赖倒置原则

 依赖倒置原则(Dependence Inversion Principle):⾼层模块不应该依赖低层模块,两者都应该依赖其抽象.不可分割的原⼦逻辑就是低层模式,原子逻辑组装成的就是⾼层模块

        模块间依赖通过抽象(接口)发生,具体类之间不直接依赖

使用建议:每个类都尽量有抽象类,任何类都不应该从具体类派⽣。尽量不要重写基类的方法。结合里⽒替换原则使用。

用例:奔驰车司机类--只能开奔驰;司机类--给什么车,就开什么车;开车的⼈:司机--依赖于抽象

1.5 迪米特法则

迪⽶特法则(Law of Demeter),⼜叫“最少知道法则”:尽量减少对象之间的交互,从⽽减⼩类之间的耦合。⼀个对象应该对其他对象有最少的了解。 对类的低耦合提出了明确的要求——只和直接的朋友交流,朋友之间也是有距离的。⾃⼰的就是⾃⼰的(如果⼀个⽅法放在本类 中,既不增加类间关系,也对本类不产⽣负⾯影响,那就放置在本类中)。

⽤例:⽼师让班⻓点名--⽼师给班⻓⼀个名单,班⻓完成点名勾选,返回结果,⽽不是班⻓点名,⽼师勾选

1.6 接⼝隔离原则

 接⼝隔离原则(Interface Segregation Principle):客⼾端不应该依赖它不需要的接口,类间的依赖关系应该建立在最⼩的接口上

使用建议:接⼝设计尽量精简单⼀,但是不要对外暴露没有实际意义的接⼝。

用例:修改密码,不应该提供修改⽤⼾信息接⼝,⽽就是单⼀的最⼩修改密码接⼝,更不要暴露数据库操作

1.7 总结

 从整体上来理解六⼤设计原则,可以简要的概括为⼀句话,⽤抽象构建框架,⽤实现扩展细节,具体 到每⼀条设计原则,则对应⼀条注意事项:

单⼀职责原则告诉我们实现类要职责单⼀;

⾥⽒替换原则告诉我们不要破坏继承体系;

依赖倒置原则告诉我们要⾯向接⼝编程;

接⼝隔离原则告诉我们在设计接⼝的时候要精简单⼀;

迪⽶特法则告诉我们要降低耦合;

开闭原则是总纲,告诉我们要对扩展开放,对修改关闭。

二、单例模式

    ⼀个类只能创建⼀个对象,即单例模式,该设计模式可以保证系统中该类只有⼀个实例,并提供⼀个访问它的全局访问点,该实例被所有程序模块共享。(像数据库连接池、日志系统、线程池会特别需要)

1、把这些数据都放到一个类里面,把这个类设计成单例类

       ⽐如在某个服务器程序中,该服务器的配置信息存放在⼀个⽂件中,这些配置数据由⼀个单例对象统⼀读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种⽅式简化了在复杂环境下的配置管理。 

2、构造、拷贝构造和赋值重载都封死,提供一个static公有获取单例对象的函数

3、单例模式有两种实现模式:饿汉模式和懒汉模式 

2.1 饿汉模式

 饿汉模式:程序启动时就会创建⼀个唯⼀的实例对象。

      因为单例对象已经确定,所以⽐较适⽤于多线程环境中,多线程获取单例对象不需要加锁,可以有效的避免资源竞争,提⾼性能。

// 饿汉模式 
#include<iostream>
class Singleton {
private:static Singleton _eton;
private:Singleton(){std::cout<<"Singleton()"<<std::endl;}~Singleton(){std::cout<<"~Singleton()"<<std::endl;}
public:Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static Singleton& getInstance() {return _eton;}
};
Singleton Singleton::_eton;
int main()
{std::cout<<"hello"<<std::endl;Singleton &s1 = Singleton::getInstance();return 0;
}

 

2.2 懒汉模式 

      第⼀次使用要使用单例对象的时候创建实例对象。如果单例对象构造特别耗时或者耗费资源(加载插件、加载网络资源等),可以选择懒汉模式,在第⼀次使用的时候才创建对象

2.2.1 C++11之前 

       常规实现思路:定义一个静态对象的指针,在访问接口的时候发现他为空才去new一个对象,但是这样会存在线程竞争需要加锁,但是加锁又会有锁冲突,就还得再加一层检测

#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
class Singleton
{
public:static Singleton* GetInstance() {// 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全if (nullptr == m_pInstance) { //这是为了保证效率m_mtx.lock();if (nullptr == m_pInstance)  m_pInstance = new Singleton(); //保证线程安全m_mtx.unlock();}return m_pInstance;}
// 一般全局都要使用单例对象,所以单例对象一般不需要显示释放// 有些特殊场景,想显示释放一下static void DelInstance(){m_mtx.lock();if (_ins){delete _ins;_ins = nullptr;}m_mtx.unlock();}// 实现一个内嵌垃圾回收类    class CGarbo {  //因为该静态对象是一个指针所以并不会调用析构 所以我们用一个内部类帮我们调用public:
~CGarbo(){DelInstance();}};// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象static CGarbo Garbo;
~Singleton(){// 一般不需要  但是有些时候会有持久化需求// 比如要求程序结束时,将数据写到文件,单例对象析构时持久化就比较好}
private:// 构造函数私有Singleton(){};// 防拷贝Singleton(Singleton const&);Singleton& operator=(Singleton const&);static Singleton* m_pInstance; // 单例对象指针static mutex m_mtx;   //互斥锁
};
Singleton* Singleton::m_pInstance = nullptr;
Singleton::CGarbo Garbo;
mutex Singleton::m_mtx;int main()
{thread t1([]{cout << &Singleton::GetInstance() << endl; });thread t2([]{cout << &Singleton::GetInstance() << endl; });t1.join();t2.join();cout << &Singleton::GetInstance() << endl;cout << &Singleton::GetInstance() << endl;return 0;
}

注意1:指针并不会自动调用析构!!所以需要我们手动进行delete!(如果是饿汉创建的是对象的话会自己析构,但是懒汉必须得是指针,所以必须手动delete)但是我们很可能会忘记,所以我们可以搞一个内嵌的垃圾回收类,然后由他的析构去帮助我们delete!他的变量也是静态的,声明周期和单例对象一样!!

注意2:单例对象一般不需要显式去delete,而是会随着程序结束而结束,但是有些场景下我们可能会需要显式释放一下,那我们就可以去封装一个Delinstance,来帮我们完成delete的操作

注意3:析构函数一般也不需要写,但是在有些情况比方说需要有一些持久化保存,比方说程序结束的时候需要将数据写到文件!!那么这个操作就要写到析构函数里面 

注意4:构造的时候双判断,第一个判断是为了提高效率的(如果没有也行,但是会导致每一次调用无论创不创建都要经过加锁和解锁,如果有的话那么只有第一次创建的会加锁解锁),第二个判断是为了线程安全加锁的!

2.2.2 C++11之后

特殊思路:这⾥介绍的是《Effective C++》⼀书作者ScottMeyers提出的⼀种更加优雅简便的单例模式Meyers' Singleton in C++。C++11 Static local variables特性以确保C++11起,局部静态变量将能够在满⾜thread-safe的前提下唯⼀地被构造和析构

//懒汉模式
#include<iostream>
class Singleton {
private:Singleton(){std::cout<<"Singleton()"<<std::endl;}~Singleton(){std::cout<<"~Singleton()"<<std::endl;}
public:Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static Singleton& getInstance() {static Singleton _eton;return _eton; }
};
int main()
{std::cout<<"hello"<<std::endl;Singleton &s1 = Singleton::getInstance();return 0;
}

2.3 饿汉vs懒汉(优缺点) 

 饿汉的缺点:

1、如果单例对象初始化很慢(如初始化动作多,还会伴随一些IO行为,比如读取配置文件等),那么在main函数之前就要申请。由于暂时不需要使用确占用了资源,其次程序启动速度会受影响

2、如果两个单例都是饿汉,并且有依赖关系,要求单例1再创建,单例2再创建,饿汉无法控制顺序,懒汉才可以。

饿汉的优点:更加简单!

懒汉完美解决了饿汉的所有问题,只不过设计上相对复杂一点!

三、工厂模式

     工厂模式是⼀种创建型设计模式,它提供了⼀种创建对象的最佳⽅式。在⼯⼚模式中,我们创建对象是不会对上层暴露创建逻辑,⽽是通过使⽤⼀个共同结构来指向新创建的对象,以此实现创建-使用的 分离。

3.1 简单工厂模式

简单⼯⼚模式:简单⼯⼚模式实现由⼀个工厂对象通过类型决定创建出来指定产品类的实例。假设 有个⼯⼚能⽣产出⽔果,当客⼾需要产品的时候明确告知⼯⼚⽣产哪类⽔果,工厂需要接收⽤⼾提 供的类别信息,当新增产品的时候,工厂内部去添加新产品的生产方式。

//简单⼯⼚模式:通过参数控制可以⽣产任何产品

// 优点:简单粗暴,直观易懂。使⽤⼀个⼯⼚⽣产同⼀等级结构下的任意产品

// 缺点:

// 1. 所有东西⽣产在⼀起,产品太多会导致代码量庞⼤

// 2. 开闭原则遵循(开放拓展,关闭修改)的不是太好,要新增产品就必须修改⼯⼚⽅法。

#include <iostream>
#include <string>
#include <memory>
class Fruit {public:Fruit(){}virtual void show() = 0;
};
class Apple : public Fruit {public:
Apple() {}virtual void show() {std::cout << "我是一个苹果" << std::endl;}
};
class Banana : public Fruit {public:Banana() {}virtual void show() {std::cout << "我是一个香蕉" << std::endl;}
};
class FruitFactory {public:static std::shared_ptr<Fruit> create(const std::string &name) {if (name == "苹果") {return std::make_shared<Apple>();}else if(name == "香蕉") {return std::make_shared<Banana>();}return std::shared_ptr<Fruit>();}
};
int main()
{std::shared_ptr<Fruit> fruit = FruitFactory::create("苹果");fruit->show();fruit = FruitFactory::create("香蕉");fruit->show();return 0;
}

   这个模式的结构和管理产品对象的⽅式⼗分简单,但是它的扩展性⾮常差,当我们需要新增产品的时候,就需要去修改⼯⼚类新增⼀个类型的产品创建逻辑,违背了开闭原则。  

3.2 工厂方法模式

⼯⼚⽅法模式:在简单⼯⼚模式下新增多个⼯⼚,多个产品,每个产品对应⼀个⼯⼚。假设现在有A、B两种产品,则开两个⼯⼚,⼯⼚A负责⽣产产品A,⼯⼚B负责⽣产产品B,⽤⼾只知道产品 的⼯⼚名,⽽不知道具体的产品信息,⼯⼚不需要再接收客⼾的产品类别,⽽只负责⽣产产品。

//⼯⼚⽅法:定义⼀个创建对象的接⼝,但是由⼦类来决定创建哪种对象,使⽤多个⼯⼚分别⽣产指定 的固定产品

// 优点:

// 1. 减轻了⼯⼚类的负担,将某类产品的⽣产交给指定的⼯⼚来进⾏

// 2. 开闭原则遵循较好,添加新产品只需要新增产品的⼯⼚即可,不需要修改原先的⼯⼚类

// 缺点:对于某种可以形成⼀组产品族的情况处理较为复杂,需要创建⼤量的⼯⼚类.

#include <iostream>
#include <string>
#include <memory>
class Fruit {public:Fruit(){}virtual void show() = 0;
};
class Apple : public Fruit {public:Apple() {}virtual void show() {std::cout << "我是一个苹果" << std::endl;}private:std::string _color;
};
class Banana : public Fruit {public:Banana() {}virtual void show() {std::cout << "我是一个香蕉" << std::endl;}
};
class FruitFactory {public:virtual std::shared_ptr<Fruit> create() = 0;
};
class AppleFactory : public FruitFactory {public:virtual std::shared_ptr<Fruit> create() {return std::make_shared<Apple>();}
};
class BananaFactory : public FruitFactory {public:virtual std::shared_ptr<Fruit> create() {return std::make_shared<Banana>();}
};
int main()
{std::shared_ptr<FruitFactory> factory(new AppleFactory());std::shared_ptr<Fruit> fruit = factory->create();fruit->show();factory.reset(new BananaFactory());fruit = factory->create();fruit->show();return 0;
}

 ⼯⼚⽅法模式每次增加⼀个产品时,都需要增加⼀个具体产品类和⼯⼚类,这会使得系统中类的个数成倍增加,在⼀定程度上增加了系统的耦合度。

3.3 抽象工厂模式

抽象⼯⼚模式:⼯⼚⽅法模式通过引⼊⼯⼚等级结构,解决了简单⼯⼚模式中⼯⼚类职责太重的问 题,但由于⼯⼚⽅法模式中的每个⼯⼚只⽣产⼀类产品,可能会导致系统中存在⼤量的⼯⼚类,势 必会增加系统的开销。此时,我们可以考虑将⼀些相关的产品组成⼀个产品族(位于不同产品等级 结构中功能相关联的产品组成的家族),由同⼀个⼯⼚来统⼀⽣产,这就是抽象⼯⼚模式的基本思 想。

//抽象⼯⼚:围绕⼀个超级⼯⼚创建其他⼯⼚。每个⽣成的⼯⼚按照⼯⼚模式提供对象。

// 思想:将⼯⼚抽象成两层,抽象⼯⼚ & 具体⼯⼚⼦类, 在⼯⼚⼦类种⽣产不同类型的⼦产品

#include <iostream>
#include <string>
#include <memory>
class Fruit {public:Fruit(){}virtual void show() = 0;
};
class Apple : public Fruit {public:Apple() {}virtual void show() {std::cout << "我是一个苹果 " << std::endl;}private:std::string _color;
};
class Banana : public Fruit {public:Banana() {}virtual void show() {std::cout << "我是一个香蕉" << std::endl;
}
};
class Animal {public:virtual void voice() = 0;
};
class Lamp: public Animal {public:void voice() { std::cout << "咩咩咩\n"; }
};
class Dog: public Animal {public:void voice() { std::cout << "汪汪汪\n"; }
};
class Factory {public:virtual std::shared_ptr<Fruit> getFruit(const std::string &name) = 0;virtual std::shared_ptr<Animal> getAnimal(const std::string &name) = 0;
};
class FruitFactory : public Factory {public:virtual std::shared_ptr<Animal> getAnimal(const std::string &name) {return std::shared_ptr<Animal>();}virtual std::shared_ptr<Fruit> getFruit(const std::string &name) {if (name == "苹果") {return std::make_shared<Apple>();}else if(name == "⾹蕉") {return std::make_shared<Banana>();}return std::shared_ptr<Fruit>();}
};
class AnimalFactory : public Factory {public:virtual std::shared_ptr<Fruit> getFruit(const std::string &name) {return std::shared_ptr<Fruit>();}virtual std::shared_ptr<Animal> getAnimal(const std::string &name) {if (name == "⼩⽺") {return std::make_shared<Lamp>();}else if(name == "⼩狗") {
return std::make_shared<Dog>();}return std::shared_ptr<Animal>();}
};
class FactoryProducer {public:static std::shared_ptr<Factory> getFactory(const std::string &name) {if (name == "动物") {return std::make_shared<AnimalFactory>();}else {return std::make_shared<FruitFactory>();}}
};
int main()
{std::shared_ptr<Factory> fruit_factory = FactoryProducer::getFactory("⽔果");std::shared_ptr<Fruit> fruit = fruit_factory->getFruit("苹果");fruit->show();fruit = fruit_factory->getFruit("⾹蕉");fruit->show();std::shared_ptr<Factory> animal_factory = FactoryProducer::getFactory("动物");std::shared_ptr<Animal> animal = animal_factory->getAnimal("⼩⽺");animal->voice();animal = animal_factory->getAnimal("⼩狗");animal->voice();return 0;
}

 

抽象⼯⼚模式适⽤于⽣产多个⼯⼚系列产品衍⽣的设计模式,增加新的产品等级结构复杂,需要对原 有系统进⾏较⼤的修改,甚⾄需要修改抽象层代码,违背了“开闭原则”。 

四、建造者模式

建造者模式是⼀种创建型设计模式,使⽤多个简单的对象⼀步⼀步构建成⼀个复杂的对象,能够将⼀ 个复杂的对象的构建与它的表⽰分离,提供⼀种创建对象的最佳⽅式。主要⽤于解决对象的构建过于复杂的问题。

      工厂模式核心在于创建一个对象,而建造者模式核心在于构建一个对象!!当构建对像过于复杂且对内部零件有顺序要求时,就可以用建造者模式

建造者模式主要基于四个核⼼类实现:

• 抽象产品类:

• 具体产品类:⼀个具体的产品对象类

• 抽象Builder类:创建⼀个产品对象所需的各个部件的抽象接⼝

• 具体产品的Builder类:实现抽象接⼝,构建各个部件

• 指挥者Director类:统⼀组建过程,提供给调⽤者使⽤,通过指挥者来构造产品

#include <iostream>
#include <memory>
/*抽象电脑类*/ 
class Computer {public:using ptr = std::shared_ptr<Computer>;Computer() {}void setBoard(const std::string &board) {_board = board;}void setDisplay(const std::string &display) {_display = display;}virtual void setOs() = 0;std::string toString() {std::string computer = "Computer:{\n";computer += "\tboard=" + _board + ",\n"; computer += "\tdisplay=" + _display + ",\n"; computer += "\tOs=" + _os + ",\n"; computer += "}\n";return computer;}protected:std::string _board;std::string _display;std::string _os;
};
/*具体产品类*/ 
class MacBook : public Computer {public:using ptr = std::shared_ptr<MacBook>;MacBook() {}virtual void setOs() {_os = "Max Os X12";}
};
/*抽象建造者类:包含创建⼀个产品对象的各个部件的抽象接⼝*/ 
class Builder {public:using ptr = std::shared_ptr<Builder>;
virtual void buildBoard(const std::string &board) = 0;virtual void buildDisplay(const std::string &display) = 0;virtual void buildOs() = 0;virtual Computer::ptr build() = 0;
};
/*具体产品的具体建造者类:实现抽象接⼝,构建和组装各个部件*/ 
class MacBookBuilder : public Builder {public:using ptr = std::shared_ptr<MacBookBuilder>;MacBookBuilder(): _computer(new MacBook()) {}virtual void buildBoard(const std::string &board) {_computer->setBoard(board);}virtual void buildDisplay(const std::string &display) {_computer->setDisplay(display);}virtual void buildOs() {_computer->setOs();}virtual Computer::ptr build() {return _computer;}private:Computer::ptr _computer;
};
/*指挥者类,提供给调⽤者使⽤,通过指挥者来构造复杂产品*/ 
class Director {public:Director(Builder* builder):_builder(builder){}void construct(const std::string &board, const std::string &display) {_builder->buildBoard(board);_builder->buildDisplay(display);_builder->buildOs();}private:Builder::ptr _builder;
};
int main()
{Builder *buidler = new MacBookBuilder();std::unique_ptr<Director> pd(new Director(buidler));pd->construct("英特尔主板", "VOC显示器");Computer::ptr computer = buidler->build();std::cout << computer->toString();
return 0;
}

 

五、代理模式

      代理模式指代理控制对其他对象的访问,也就是代理对象控制对原对象的引⽤。在某些情况下,⼀个 对象不适合或者不能直接被引⽤访问,⽽代理对象可以在客⼾端和⽬标对象之间起到中介的作⽤。 代理模式的结构包括⼀个是真正的你要访问的对象(⽬标类)、⼀个是代理对象。⽬标对象与代理对象实 现同⼀个接⼝,先访问代理类再通过代理类访问⽬标对象。代理模式分为静态代理、动态代理:

静态代理指的是,在编译时就已经确定好了代理类和被代理类的关系。也就是说,在编译时就已经 确定了代理类要代理的是哪个被代理类。

动态代理指的是,在运⾏时才动态⽣成代理类,并将其与被代理类绑定。这意味着,在运⾏时才能 确定代理类要代理的是哪个被代理类。

 以租房为例,房东将房⼦租出去,但是要租房⼦出去,需要发布招租启⽰,带⼈看房,负责维修,这 些⼯作中有些操作并⾮房东能完成,因此房东为了图省事,将房⼦委托给中介进⾏租赁。代理模式实现:

/*房东要把一个房子通过中介租出去理解代理模式*/ 
#include <iostream>
#include <string>
class RentHouse {public:virtual void rentHouse() = 0;
};
/*房东类:将房子租出去*/ 
class Landlord : public RentHouse {public: void rentHouse() {std::cout << "将房子租出去\n";}
};
/*中介代理类:对租房子进行功能加强,实现租房以外的其他功能*/ 
class Intermediary : public RentHouse {public:void rentHouse() {std::cout << "发布招租启示\n";std::cout << "带人看房\n";_landlord.rentHouse();std::cout << "负责租后维修\n";}
private:Landlord _landlord;
};
int main()
{Intermediary intermediary;intermediary.rentHouse();return 0;
}

 

 

相关文章:

同步异步日志系统:设计模式

设计模式是前辈们对代码开发经验的总结&#xff0c;是解决特定问题的⼀系列套路。它不是语法规定&#xff0c;⽽是⼀ 套⽤来提⾼代码可复⽤性、可维护性、可读性、稳健性以及安全性的解决⽅案。 为什么会产生设计模式这样的东西呢&#xff1f;就像人类历史发展会产生兵法。最开…...

【GO基础学习】Gin 框架中间件的详解

文章目录 中间件详解中间件执行全局中间件路由级中间件运行流程中间件的链式执行中断流程 代码示例 gin框架总结 中间件详解 Gin 框架中间件是其核心特性之一&#xff0c;主要用于对 HTTP 请求的处理进行前置或后置的逻辑插入&#xff0c;例如日志记录、身份认证、错误处理等。…...

ubuntu停止.netcore正在运行程序的方法

在Ubuntu系统中停止正在运行的.NET Core程序&#xff0c;你可以使用以下几种方法&#xff1a; 使用kill命令&#xff1a; 如果你知道.NET Core程序的进程ID&#xff08;PID&#xff09;&#xff0c;你可以直接使用kill命令来停止它。首先&#xff0c;使用ps命令配合grep来查找.…...

图神经网络_图嵌入_Struc2Vec

0 背景 之前的node embedding方式&#xff0c;都是基于近邻关系&#xff0c;但是有些节点没有近邻&#xff0c;也有结构相似性。如图中的u、v节点。 struc2vec算法适用于捕获结构相似性。 1 相似度&#xff08;距离&#xff09;计算 1.1 公式 f k ( u , v ) f k − 1 ( u …...

LabVIEW应用在工业车间

LabVIEW作为一种图形化编程语言&#xff0c;以其强大的数据采集和硬件集成功能广泛应用于工业自动化领域。在工业车间中&#xff0c;LabVIEW不仅能够实现快速开发&#xff0c;还能通过灵活的硬件接口和直观的用户界面提升生产效率和设备管理水平。尽管其高成本和初期学习门槛可…...

js-000000000000

1、js书写的位置 - 内部 <body> <!-- 习惯把 js 放到 /body 的后面 --> <script> console.log(这是内部 js 的书写位置) alert(内部js) </script> </body> <body><!-- 习惯把 js 放到 /body 的后面 --><script>console.log(这…...

【微信小程序】3|首页搜索框 | 我的咖啡店-综合实训

首页-搜索框-跳转 引言 在微信小程序中&#xff0c;首页的搜索框是用户交互的重要入口。本文将通过“我的咖啡店”小程序的首页搜索框实现&#xff0c;详细介绍如何在微信小程序中创建和处理搜索框的交互。 1. 搜索函数实现 onClickInput函数在用户点击搜索框时触发&#x…...

虚幻引擎是什么?

Unreal Engine&#xff0c;是一款由Epic Games开发的游戏引擎。该引擎主要是为了开发第一人称射击游戏而设计&#xff0c;但现在已经被成功地应用于开发模拟游戏、恐怖游戏、角色扮演游戏等多种不同类型的游戏。虚幻引擎除了被用于开发游戏&#xff0c;现在也用于电影的虚拟制片…...

分布式光纤传感|分布式光纤测温|线型光纤感温火灾探测器DTS|DTS|DAS|BOTDA的行业16年的总结【2024年】

背景&#xff1a; 从2008年&#xff0c;从事分布式光纤传感行业已经过了16年时间了&#xff0c;依稀记得2008年&#xff0c;看的第一遍论文就是中国计量大学张在宣老爷子的分布式光纤测温综述&#xff0c;我的经历算是行业内极少数最丰富的之一。混过学术圈&#xff1a; 发表…...

【无标题】学生信息管理系统界面

网页是vue框架&#xff0c;后端直接python写的没使用框架...

ES7+ React/Redux/GraphQL/React-Native snippets 使用指南

VS Code React Snippets 使用指南 目录 简介基础方法React 相关React Native 相关Redux 相关PropTypes 相关控制台相关React 组件相关 简介 ES7 React/Redux/GraphQL/React-Native snippets 是一个用于 VS Code 的代码片段插件&#xff0c;它提供了大量用于 React 开发的代…...

Java中三大构建工具的发展历程(Ant、Maven和Gradle)

&#x1f438; 背景 我们要写一个Java程序&#xff0c;一般的步骤是编译&#xff0c;测试&#xff0c;打包。 这个构建的过程&#xff0c;如果文件比较少&#xff0c;我们可以手动使用java, javac,jar命令去做这些事情。但当工程越来越大&#xff0c;文件越来越多&#xff0c…...

【国产NI替代】32振动/电压(配置复合型)高精度终端采集板卡,应用于复杂的大型测量场景

32振动/电压&#xff08;配置复合型&#xff09;高精度终端采集板卡 采用 EP4CE115F29I7 型号的 FPGA &#xff0c;是一款 高精度&#xff0c;多通道动态信号采集器&#xff0c;主要应用 在复杂的大型测量并对成本要求不敏感的场 合&#xff0c;默认具备 8 个测量板卡&#…...

服务器上加入SFTP------(小白篇 1)

在服务器上配置 SFTP (基于 SSH 的文件传输协议) 通常比传统 FTP 更安全&#xff0c;因为它默认加密通信。以下是详细的配置步骤&#xff0c;以 Ubuntu 或 CentOS 为例。 1.服务器上加入SFTP------(小白篇 1) 2.加入SFTP 用户------(小白篇 2) 3.代码加入SFTP JAVA —&#…...

突围边缘:OpenAI开源实时嵌入式API,AI触角延伸至微观世界

当OpenAI宣布开源其名为openai-realtime-embedded-sdk的实时嵌入式API时&#xff0c;整个科技界都为之震惊。这一举动意味着&#xff0c;曾经遥不可及的强大AI能力&#xff0c;如今可以被嵌入到像ESP32这样的微型控制器中&#xff0c;真正地将AI的触角延伸到了物联网和边缘计算…...

【含开题报告+文档+PPT+源码】基于SpringBoot+Vue的影视网站系统的设计与实现

开题报告 随着互联网的快速发展和普及&#xff0c;人们对于娱乐和信息的需求越来越大。影视网站作为一种提供短视频、影视、电视剧、综艺节目等视频资源的网站&#xff0c;受到了广大用户的喜爱。然而&#xff0c;现有的影视网站系统仍然存在着一些安全性不强&#xff0c;用户…...

前端技术(26) : 全年排班日历

来源: 通义千问 效果图 代码 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>年度日历</title><style>body {font-family: Arial, sans-serif;}.calendar-container {margin: 20px au…...

Linux网络——TCP的运用

系列文章目录 文章目录 系列文章目录一、服务端实现1.1 创建套接字socket1.2 指定网络接口并bind2.3 设置监听状态listen2.4 获取新链接accept2.5 接收数据并处理&#xff08;服务&#xff09;2.6 整体代码 二、客户端实现2.1 创建套接字socket2.2 指定网络接口2.3 发起链接con…...

Elasticsearch 数据存储底层机制详解

Elasticsearch 数据存储底层机制详解 Elasticsearch 的底层存储机制依赖 Lucene 来实现数据的组织和管理。下面从数据存储的 流转过程 和 管理机制 两个方面来详细说明。 1. 数据存储流程 当一个文档通过 REST API 被写入 Elasticsearch 时&#xff0c;会经历以下流程&#x…...

Spring Boot 中 Map 的最佳实践

在Spring Boot中使用Map时&#xff0c;请遵循以下最佳实践: 1.避免在Controller中 直接使用Map。应该使用RequestBody 接收-个DTO对象或者 RequestParam接收参数&#xff0c;然后在Service中处 理Map。 2.避免在Service中 直接使用原始的Map。应该使用Autowired 注入-个专门…...

es6 字符串每隔几个中间插入一个逗号

const insertCommaEveryNChars (str, n) > {// 将字符串转换为数组&#xff0c;以便我们可以更容易地操作每个字符const chars str.split();// 使用map遍历数组&#xff0c;并在每隔n个字符后插入逗号const result chars.map((char, index) > {// 检查当前位置是否是n…...

区块链共识机制深度揭秘:从PoW到PoS,谁能主宰未来?

区块链的技术背后&#xff0c;最大的挑战之一就是如何让多个分布在全球各地的节点在没有中心化管理者的情况下达成一致&#xff0c;确保数据的一致性和安全性。这一切都依赖于区块链的核心——共识机制。共识机制不仅决定了区块链的安全性、效率和去中心化程度&#xff0c;还对…...

SQL Server 新建 用户 登录失败。 (Microsoft SQL Server,错误: 18456)

新建用户后用SQLserver shen身份验证一直提示用户登录用户 登录失败。 (Microsoft SQL Server,错误: 18456)。 问题&#xff1a; 新建标题: 连接到服务器 无法连接到 DESKTOP-GKBXLEE。 其他信息: 用户 ‘’ 登录失败。 (Microsoft SQL Server&#xff0c;错误: 18456) 解…...

AW36518芯片手册解读(3)

接前一篇文章&#xff1a;AW36518芯片手册解读&#xff08;2&#xff09; 二、详述 3. 功能描述 &#xff08;1&#xff09;上电复位 当电源电压VIN降至预定义电压VPOR&#xff08;典型值为2.0V&#xff09;以下时&#xff0c;该设备会产生复位信号以执行上电复位操作&#x…...

有没有免费提取音频的软件?音频编辑软件介绍!

出于工作和生活娱乐等原因&#xff0c;有时候我们需要把音频单独提取出来&#xff08;比如歌曲伴奏、人声清唱等、乐器独奏等&#xff09;。要提取音频必须借助音频处理软件&#xff0c;那么有没有免费提取音频的软件呢&#xff1f;下面我们将为大家介绍几款免费软件&#xff0…...

一次医院RIS系统的升级

2020-03-11 目录 数据库升级... 1 数据结构升级... 1 系统配置... 2 WEB服务器准备... 3 启动ASP.NET State Service服务... 3 检查IIS. 4 发布站点... 4 添加应用程序池... 4 发布网站... 5 处理打印模板... 6 web.config的配置... 6 处理图片文件目录... 6 修改W…...

clickhouse测试报告

​一、背景 针对当前实施的项目&#xff0c;面临着两个主要挑战&#xff1a;一是需要存储更详细的原始数据和中间数据&#xff0c;二是现有基于MySQL的数据存储解决方案在数据量增长时性能受限&#xff0c;特别是在进行跨年历史数据的即时分析时。为了解决这些问题&#xf…...

Elasticsearch安装和数据迁移

Elasticsearch安装和数据迁移 Elasticsearch安装 下载并解压Elasticsearch 首先下载Elasticsearch的tar.gz文件&#xff0c;并将其解压&#xff1a; wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.8.2-linux-x86_64.tar.gz tar -xzf elastics…...

K8s证书过期

part of the existing bootstrap client certificate is expired: 2023-11-27 12:44:12 0000 UTC 查看运行日志&#xff1a; journalctl -xefu kubelet 重新生成证书&#xff1a; #重新生成证书 kubeadm alpha certs renew all #备份旧的配置文件 mv /etc/kubernetes/*.conf…...

JSONException:java.lang.String cannot be converted to JSONObject异常的解决方法

在用org.json.JSONObject解析从网络获取的json数据时&#xff0c;遇到JSONException:java.lang.String cannot be converted to JSONObject&#xff0c;打印字符串&#xff0c;查看json字符串没有问题&#xff0c;研究了好长时间&#xff0c;终于找到问题&#xff0c;造成问题的…...

[源码解析] 模型并行分布式训练Megatron (2) --- 整体架构

link [源码解析] 模型并行分布式训练Megatron (2) --- 整体架构 目录 [源码解析] 模型并行分布式训练Megatron (2) --- 整体架构 0x00 摘要0x01 启动 1.1 分布式启动1.2 构造基础 1.2.1 获取模型1.2.2 获取数据集1.2.3 步进函数 1.2.3.1 广播数据0x02 Pretrain0x03 初始化 3.1 …...

kubeadm搭建k8s集群

前置环境&#xff1a; 准备三台虚拟机 192.168.1.104&#xff08;用来做k8s的mater节点&#xff09; 192.168.1.105&#xff08;节点node2&#xff09; 192.168.1.109&#xff08;节点node3&#xff09; 关闭防火墙 systemctl stop firewalld systemctl disable firewalld安装…...

家用无线路由器的 2.4GHz 和 5GHz

家中的无线路由器 WiFi 名称有两个&#xff0c;一个后面带有 “5G” 的标记&#xff0c;这让人产生疑问&#xff1a;“连接带‘5G’的 WiFi 是不是速度更快&#xff1f;” 实际上&#xff0c;这里的 “5G” 并不是移动通信中的 5G 网络&#xff0c;而是指路由器的工作频率为 5G…...

#渗透测试#漏洞挖掘#红蓝攻防#漏洞挖掘#未授权漏洞-Es未授权漏洞

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…...

Windows 使用 非安装版MySQL 8

1.下载MySQL 8 https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.40-winx64.zip 2.创建my.ini 下载解压后&#xff0c;发现根目录没有my.ini文件&#xff0c;需手动创建 my.ini # For advice on how to change settings please see # http://dev.mysql.com/doc/refma…...

nginx Rewrite 相关功能

一、Nginx Rewrite 概述 定义 Nginx 的 Rewrite 模块允许对请求的 URI 进行重写操作。它可以基于一定的规则修改请求的 URL 路径&#xff0c;然后将请求定向到新的 URL 地址&#xff0c;这在很多场景下都非常有用&#xff0c;比如实现 URL 美化、网站重构后的 URL 跳转等。主要…...

2024年AI相关的论文写作经验(附实践资料下载)

在撰写AI相关的论文时&#xff0c;以下是一些实用的经验和技巧&#xff1a; 明确写作目标&#xff1a;在开始写作之前&#xff0c;明确你的论文类型&#xff08;期刊论文、毕业论文等&#xff09;和目标&#xff0c;这将影响你的写作方式和工具选择。 AI辅助文献检索&#xff…...

List详解

List详解 在Java中&#xff0c;List是一个接口&#xff0c;它继承自Collection接口。List接口为数据的有序集合提供了操作接口&#xff0c;其中可以包含重复的元素。这个接口的实现类以特定的方式存储元素&#xff0c;允许元素根据索引进行访问&#xff0c;同时还支持通过迭代…...

Flutter实现可拖拽操作Draggable

文章目录 1. Draggable 控件的构造函数主要参数&#xff1a; 2. Draggable 的工作原理3. 常见用法示例 1&#xff1a;基本的拖拽控件解释&#xff1a;示例 2&#xff1a;与 DragTarget 配合使用解释&#xff1a; 4. Draggable 的回调详解5. 总结 Draggable 是 Flutter 中一个用…...

【QSS样式表 - ⑥】:QPushButton控件样式

文章目录 QPushBUtton控件样式QSS示例 QPushBUtton控件样式 常用子控件 常用伪状态 QSS示例 代码&#xff1a; QPushButton {background-color: #99B5D1;color: white;font-weigth: bold;border-radius: 20px; }QPushButton:hover {background-color: red; }QPushButton:p…...

DPO(Direct Preference Optimization)算法解释:中英双语

中文版 DPO paper: https://arxiv.org/pdf/2305.18290 DPO 算法详解&#xff1a;从理论到实现 1. 什么是 DPO&#xff1f; DPO&#xff08;Direct Preference Optimization&#xff09;是一种直接基于人类偏好进行优化的算法&#xff0c;旨在解决从人类偏好数据中训练出表现…...

springboot495基于java的物资综合管理系统的设计与实现(论文+源码)_kaic

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统物资综合管理系统信息管理难度大&#xff0c;容错率低&am…...

JavaScript语言的编程范式

JavaScript&#xff1a;面向对象与函数式编程的双重奏 在编程世界中&#xff0c;JavaScript 无疑是一颗璀璨的明星&#xff0c;它不仅主宰着前端开发领域&#xff0c;还在后端、桌面应用、甚至物联网设备上展现出了强大的生命力。JavaScript 的魅力在于其灵活多变的编程范式&a…...

MyBatis动态 SQL 的执行原理

MyBatis 动态 SQL 是 MyBatis 框架中的一个重要特性&#xff0c;它允许开发者根据条件动态地生成不同的 SQL 语句。通过使用动态 SQL&#xff0c;开发者可以根据传入的参数动态地构建 SQL 查询&#xff0c;这样就避免了写多个 SQL 语句&#xff0c;提升了代码的灵活性和可维护性…...

PostgreSQL自带的一个命令行工具pg_waldump

pg_waldump是PostgreSQL自带的一个命令行工具&#xff0c;用于以人类可读的形式显示PostgreSQL数据库集簇的预写式日志&#xff08;Write-Ahead Logging&#xff0c;WAL&#xff09;。以下是对pg_waldump的详细介绍&#xff1a; 一、主要用途 pg_waldump主要用于调试或教育目…...

K8s 常用资源介绍

在 Kubernetes 中&#xff0c;资源指的是可以在集群中管理的对象&#xff08;Objects&#xff09;。这些资源用来定义和控制应用、服务、以及集群的状态。以下是 Kubernetes 中常见的资源及其用途介绍&#xff1a; 1. 工作负载资源&#xff08;Workloads Resources&#xff09;…...

基于 Python 大数据的拼团购物数据分析系统的设计与实现

标题:基于 Python 大数据的拼团购物数据分析系统的设计与实现 内容:1.摘要 本文设计并实现了一个基于 Python 大数据的拼团购物数据分析系统。通过对拼团购物数据的收集、清洗和分析&#xff0c;系统能够为商家提供用户行为分析、商品销售情况分析等功能&#xff0c;帮助商家更…...

finalshell密码解密

finalshell密码解密 在线网站运行java https://c.runoob.com/compile/10/ import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.MessageDigest; import java.security.N…...

利用Java爬虫速卖通按关键字搜索AliExpress商品

在这个信息爆炸的时代&#xff0c;数据的价值日益凸显。对于电商领域的从业者来说&#xff0c;能够快速获取商品信息成为了一项重要的技能。速卖通&#xff08;AliExpress&#xff09;作为全球领先的跨境电商平台&#xff0c;拥有海量的商品数据。本文将介绍如何使用Java语言编…...

每天40分玩转Django:Django缓存

一、Django缓存概述 在高并发的Web应用中,缓存是提高性能的重要手段。通过缓存频繁访问的数据,可以显著减少数据库查询和渲染模板的时间,从而加快响应速度,提升用户体验。Django提供了多层级的缓存方案,可以灵活地满足不同场景下的缓存需求。 Django支持的缓存方式包括: 视图…...