常用设计模式总结

2023-11-18

设计模式的相关知识,很多书籍和博客中都有详细总结,本文总结的目的:
1、将自己学习到的设计模式的知识按照自己的逻辑重新总结,方便查看和记忆。
2、方便让自己对设计模式中常用的知识有一个系统的认知。

设计模式

《⼤话设计模式》⼀书中提到 24 种设计模式,这 24 种设计模式没必要⾯⾯俱到,但⼀定要深⼊了解其中的⼏种,最好结合⾃⼰在实际开发过程中的例⼦进⾏深⼊的了解。

设计模式分类

设计模式分为三类:

  • 创造型模式:单例模式、⼯⼚模式、建造者模式、原型模式

  • 结构型模式:适配器模式、桥接模式、外观模式、组合模式、装饰模式、享元模式、代理模式

  • ⾏为型模式:责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、 观察者模式、状态模式、策略模式、模板⽅法模式、访问者模式。

几种常见的设计模式

  • 单例模式:保证⼀个类仅有⼀个实例,并提供⼀个访问它的全局访问点。

  • ⼯⼚模式:包括简单⼯⼚模式、抽象⼯⼚模式、⼯⼚⽅法模式

    • 简单⼯⼚模式:主要⽤于创建对象。⽤⼀个⼯⼚来根据输⼊的条件产⽣不同的类,然后根据不同类的虚函数得到不同的结果。

    • 抽象⼯⼚模式:定义了⼀个创建⼀系列相关或相互依赖的接⼝,⽽⽆需指定他们的具体类。

  • 观察者模式:定义了⼀种⼀对多的关系,让多个观察对象同时监听⼀个主题对象,主题对象发⽣变化时,会通知所有的观察者,使他们能够更新⾃⼰。

  • 装饰模式:动态地给⼀个对象添加⼀些额外的职责,就增加功能来说,装饰模式⽐⽣成派⽣类更为灵活。

单例模式

单例模式的适用场景

  • 系统只需要⼀个实例对象,或者考虑到资源消耗的太⼤⽽只允许创建⼀个对象。
  • 客户调⽤类的单个实例只允许使⽤⼀个公共访问点,除了该访问点之外不允许通过其它⽅式访问该实例(就是共有的静态⽅法)。

所以单例模式一般用在对实例数量有严格要求的地方,比如数据池,线程池,缓存,session回话等等。

构成的条件:

  • 私有化它的构造函数,以防止外界创建单例类的对象;
  • 使用类的私有静态指针变量指向类的唯一实例;
  • 使用一个公有的静态方法获取该实例。

单例模式有两种:饿汉模式和懒汉模式

《C++ 单例模式》https://zhuanlan.zhihu.com/p/37469260

《析构函数声明为私有的作用》https://blog.csdn.net/jia_xiaoxin/article/details/3348045

饿汉模式

饿汉模式(线程安全):顾名思义,饿了就饥不择⻝了,所以在单例类定义的时候就进行实例化。

在最开始的时候静态对象就已经创建完成,设计方法是类中包含⼀个静态成员指针,该指针指向该类的⼀个对象,提供⼀个公有的静态成员方法,返回该对象指针,为了使得对象唯⼀,构造函数设为私有。由于在main函数之前初始化,所以没有线程安全的问题。

#include <iostream>
#include <algorithm>
using namespace std;
class SingleInstance {
public:
    // 用户通过接口获取实例:使用 static 类成员函数
	static SingleInstance* GetInstance() {
		return &ins;
	}	
private:
    static SingleInstance ins;  // 注意此处不是指针
private:
	//涉及到创建对象的函数都设置为private
    SingleInstance(){}
	SingleInstance() { std::cout<<"SingleInstance() 饿汉"<<std::endl; }
	SingleInstance(const SingleInstance& other) {};
	SingleInstance& operator=(const SingleInstance& other) {return *this; }
    ~SingleInstance(){};
};
SingleInstance SingleInstance::ins;  // 静态成员函数定义并初始化

int main() {
	//因为不能创建对象所以通过静态成员函数的⽅法返回静态成员变量
	SingleInstance* ins = SingleInstance::GetInstance();
	return 0; 
}
//输出 SingleInstance() 饿汉
懒汉模式

懒汉(线程安全需要加锁):顾名思义,不到万不得已就不会去实例化类,也就是在第⼀次⽤到的类实例的时候才会去实例化。尽可能的晚的创建这个对象的实例,即在单例类第⼀次被引⽤的时候就将自己初始化,C++ 很多地方都有类型的思想,比如写时拷贝,晚绑定等。

原始版

class SingleInstance
{
private:
	static SingleInstance* instance;  // 与饿汉模式相比,此处是指针
private:
	SingleInstance() {};
	~SingleInstance() {};
	SingleInstance(const SingleInstance&);
	SingleInstance& operator=(const SingleInstance&);
public:
	static SingleInstance* getInstance() 
        {
		if(instance == NULL) 
			instance = new SingleInstance();
		return instance;
	}
};

// init static member
SingleInstance* SingleInstance::instance = NULL;

这种原始的方法存在内存泄露的问题,有两种解决方法:

  1. 使用智能指针
  2. 使用静态的嵌套类对象

对于第二种解决方法,代码如下:

class SingleInstance
{
private:
	static SingleInstance* instance;
private:
	SingleInstance() { };
	~SingleInstance() { };
	SingleInstance(const SingleInstance&);
	SingleInstance& operator=(const SingleInstance&);
private:
	class Deletor {
	public:
		~Deletor() {
			if(SingleInstance::instance != NULL)
				delete SingleInstance::instance;
		}
	};
	static Deletor deletor;
public:
	static SingleInstance* getInstance() {
		if(instance == NULL) {
			instance = new SingleInstance();
		}
		return instance;
	}
};
// init static member
SingleInstance* SingleInstance::instance = NULL;
SingleInstance::Deletor  SingleInstance::deletor;

在程序运行结束时,系统会调用静态成员deletor的析构函数,该析构函数会删除单例的唯一实例。使用这种方法释放单例对象有以下特征:

  • 在单例类内部定义专有的嵌套类。
  • 在单例类内定义私有的专门用于释放的静态成员。
  • 利用程序在结束时析构全局变量的特性,选择最终的释放时机。

这个代码在单线程环境下是正确无误的,但是当拿到多线程环境下时这份代码就会出现race condition, 要使其线程安全,能在多线程环境下实现单例模式,我们首先想到的是利用同步机制来正确的保护我们的shared data。

#include <pthread.h>
#include <iostream>
#include <algorithm>
using namespace std;
class SingleInstance {
public:
	static SingleInstance* GetInstance() {
		if (ins == nullptr) {
			pthread_mutex_lock(&mutex);
			if (ins == nullptr) {
				ins = new SingleInstance();
			}
			pthread_mutex_unlock(&mutex);
		}
		return ins;
	}
	~SingleInstance(){};
	//互斥锁
	static pthread_mutex_t mutex;
private:
	//涉及到创建对象的函数都设置为private
	SingleInstance() { std::cout<<"SingleInstance() 懒汉"<<std::endl; }
	SingleInstance(const SingleInstance& other) {};
	SingleInstance& operator=(const SingleInstance& other) { return *this; }
	//静态成员
	static SingleInstance* ins;
};

//懒汉式 静态变量需要定义
SingleInstance* SingleInstance::ins = nullptr;
pthread_mutex_t SingleInstance::mutex;

int main(){
	//因为不能创建对象所以通过静态成员函数的⽅法返回静态成员变量
	SingleInstance* ins = SingleInstance::GetInstance();
	delete ins;
	return 0; 
}
//输出 SingleInstance() 懒汉

工厂模式

⼀般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂⽅法和抽象工厂。

简单工厂模式

可以根据实际的参数不同返回不同的实例。同时在简单工厂模式中会定义⼀个类负责创建其他 类的实例,被创建的实例也通常具有共同的⽗类。简单工厂模式的实质是由⼀个工厂根据传⼊的参数,动态决定应该创建哪⼀个产品类(这些产品类继承⾃⼀个⽗类或接接口)的实例。

#include <iostream>
#include <pthread.h>
using namespace std;
//产品类(抽象类,不能实例化)
class Product{
public:
	Product(){};
	virtual void show()=0; //纯虚函数
};

class productA : public Product{
public:
	productA(){};
	void show(){ std::cout << "product A create!" << std::endl; };
	~productA(){};
};

class productB : public Product{
public:
	productB(){};
	void show(){ std::cout << "product B create!" << std::endl; };
	~productB(){};
};

class simpleFactory{ // ⼯⼚类
public:
	simpleFactory(){};
	Product* product(const string str){
		if (str == "productA")
			return new productA();
		if (str == "productB")
			return new productB();
		return NULL;
	};
};

int main(){
	simpleFactory obj; // 创建⼯⼚
	Product* pro; // 创建产品
	pro = obj.product("productA");
	pro->show(); // product A create!
	delete pro;
	pro = obj.product("productB");
	pro->show(); // product B create!
	delete pro;
	return 0; 
}

工厂模式⽬的就是代码解耦,如果我们不采⽤工厂模式,如果要创建产品 A、B,通常做法采⽤⽤ switch…case语句,那么想⼀想后期添加更多的产品进来,我们不是要添加更多的switch…case 吗?这样就很麻烦,⽽且也不符合设计模式中的开放封闭原则

抽象工厂模式

为了进⼀步解耦,在简单工厂的基础上发展出了抽象工厂模式,即连工厂都抽象出来,实现了 进⼀步代码解耦。

#include <iostream>
#include <pthread.h>
using namespace std;
//产品类(抽象类,不能实例化)
class Product{
public:
	Product(){}
	virtual void show()=0; //纯虚函数
};

class Factory{//抽象类
public:
	virtual Product* CreateProduct()=0;//纯虚函数
};

//产品A
class ProductA:public Product{
public:
	ProductA(){}
	void show(){ std::cout<<"product A create!"<<std::endl; };
};

//产品B
class ProductB:public Product{
public:
	ProductB(){}
	void show(){ std::cout<<"product B create!"<<std::endl; };
};

//⼯⼚类A,只⽣产A产品
class FactorA: public Factory{
public:
	Product* CreateProduct(){
		Product* product_ = nullptr;
		product_ = new ProductA();
		return product_;
	}
};

//⼯⼚类B,只⽣产B产品
class FactorB: public Factory{
public:
	Product* CreateProduct(){
		Product* product_ = nullptr;
		product_ = new ProductB();
		return product_;
	}
};

int main(){
	Product* product_ = nullptr;
	auto MyFactoryA = new FactorA();
	product_ = MyFactoryA->CreateProduct();// 调⽤产品A的⼯⼚来⽣产A产品
	product_->show();
	delete product_;
	auto MyFactoryB=new FactorB();
	product_ = MyFactoryB->CreateProduct();// 调⽤产品B的⼯⼚来⽣产B产品
	product_->show();
	delete product_;
	return 0; 
}
//输出
//product A create! product B create!

观察者模式

观察者模式定义⼀种⼀(被观察类)对多(观察类)的关系,让多个观察对象同时监听⼀个 被观察对象,被观察对象状态发⽣变化时,会通知所有的观察对象,使他们能够更新⾃⼰的状态。 观察者模式中存在两种⻆⾊:

  • **观察者:**内部包含被观察者对象,当被观察者对象的状态发⽣变化时,更新⾃⼰的状态。(接收通知更新状态)

  • **被观察者:**内部包含了所有观察者对象,当状态发⽣变化时通知所有的观察者更新⾃⼰的状态。(发送通知)

应⽤场景:

  • 当⼀个对象的改变需要同时改变其他对象,且不知道具体有多少对象有待改变时,应该考虑使⽤观察者模式;

  • ⼀个抽象模型有两个⽅⾯,其中⼀⽅⾯依赖于另⼀⽅⾯,这时可以⽤观察者模式将这两者封装在独⽴的对象中使它们各⾃独⽴地改变和复⽤。

#include <iostream>
#include <string>
#include <list>
using namespace std;
class Subject;

//观察者 基类 (内部实例化了被观察者的对象sub)
class Observer {
protected:
	string name;
	Subject *sub;
public:
	Observer(string name, Subject *sub) {
		this->name = name;
		this->sub = sub;
	}
	virtual void update() = 0;
};

class StockObserver : public Observer {
public:
	StockObserver(string name, Subject *sub) : Observer(name, sub){}
	void update();
};

class NBAObserver : public Observer {
public:
	NBAObserver(string name, Subject *sub) : Observer(name, sub){}
	void update();
};

//被观察者 基类 (内部存放了所有的观察者对象,以便状态发⽣变化时,给观察者发通知)
class Subject {
protected:
	std::list<Observer *> observers;
public:
	string action; //被观察者对象的状态
	virtual void attach(Observer *) = 0;
	virtual void detach(Observer *) = 0;
	virtual void notify() = 0;
};

class Secretary : public Subject {
	void attach(Observer *observer) {
		observers.push_back(observer);
	}
	void detach(Observer *observer) {
		list<Observer *>::iterator iter = observers.begin();
		while (iter != observers.end()) {
			if ((*iter) == observer) {
			observers.erase(iter);
			return;
			}
			++iter;
		}
	}
	void notify() {
		list<Observer *>::iterator iter = observers.begin();
		while (iter != observers.end()) {
			(*iter)->update();
			++iter;
		}
	}
};
void StockObserver::update() {
	cout << name << " 收到消息:" << sub->action << endl;
	if (sub->action == "⽼板来了!") {
		cout << "我⻢上关闭股票,装做很认真⼯作的样⼦!" << endl;
	}
}
void NBAObserver::update() {
	cout << name << " 收到消息:" << sub->action << endl;
	if (sub->action == "⽼板来了!") {
		cout << "我⻢上关闭 NBA,装做很认真⼯作的样⼦!" << endl;
	}
}
int main()
{
	Subject *BOSS = new Secretary();
	Observer *xa = new NBAObserver("xa", BOSS);
	Observer *xb = new NBAObserver("xb", BOSS);
	Observer *xc = new StockObserver("xc", BOSS);
	BOSS->attach(xz);
	BOSS->attach(xb);
	BOSS->attach(xc);
	BOSS->action = "去吃饭了!";
	BOSS->notify();
	cout << endl;
	BOSS->action = "⽼板来了!";
	BOSS->notify();
	return 0; 
}
//输出
//product A create! product B create!

装饰器模式

装饰器模式(Decorator Pattern)允许向⼀个现有的对象添加新的功能,同时⼜不改变其结构。
这种类型的设计模式属于结构型模式,它是作为现有的类的⼀个包装。
如下代码中没有改变 Car 类的内部结构,还为其增加了新的功能,这就是装饰器模式的作⽤。

#include <iostream>
#include <list>
#include <memory>
using namespace std;
//抽象构件类 Transform (变形⾦刚)
class Transform{
public:
	virtual void move() = 0;
};

//具体构件类Car
class Car : public Transform{
public:
	Car(){
		std::cout << "变形⾦刚是⼀辆⻋!" << endl;
	}
	void move(){
		std::cout << "在陆地上移动。" << endl;
	}
};

//抽象装饰类
class Changer : public Transform{
public:
	Changer(shared_ptr<Transform> transform){
		this->transform = transform;
	}
	void move(){
		transform->move();
	}
private:
	shared_ptr<Transform> transform;
};

//具体装饰类Robot
class Robot : public Changer{
public:
	Robot(shared_ptr<Transform> transform) : Changer(transform){
		std::cout << "变成机器⼈!" << std::endl;
	}
	void say(){
		std::cout << "说话!" << std::endl;
	}
};

//具体装饰类AirPlane
class Airplane : public Changer{
public:
	Airplane(shared_ptr<Transform> transform) : Changer(transform){
		std::cout << "变成⻜机!" << std::endl;
	}
	void say(){
		std::cout << "在天空⻜翔!" << std::endl;
	} 
};
int main(void){
	shared_ptr<Transform> camaro = make_shared<Car>();
	camaro->move();
	std::cout << "--------------" << endl;
	shared_ptr<Robot> bumblebee = make_shared<Robot>(camaro);
	bumblebee->move();
	bumblebee->say();
   	std::cout << "--------------" << endl;
	shared_ptr<Airplane> bumblebee1 = make_shared<Airplane>(camaro);
	bumblebee1->move();
	bumblebee1->say();
	return 0;
}
/*
输出
变形⾦刚是⼀辆⻋!
在陆地上移动。
--------------
变成机器⼈!
在陆地上移动。
说话!
--------------
变成⻜机!
在陆地上移动。
在天空⻜翔!
*/

装饰器模式的优点:

1、可以轻松对已存在的对象进行修改和包装,在被装饰者的前面或者后面添加自己的行为,而无需修改原对象。

2、可以动态、不限量地进行装饰,可以更灵活地扩展功能。

相对地,装饰器模式有很明显的缺点:

1、会加入大量的小类,即使只添加一个功能,也要额外创建一个类,使得程序更复杂。

2、增加代码复杂度,使用装饰器模式不但需要实例化组件,还要把组件包装到装饰者中。

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

常用设计模式总结 的相关文章

  • 如何将非静态类成员“std::bind”绑定到 Win32 回调函数“WNDPROC”?

    我正在尝试将非静态类成员绑定到标准WNDPROC http msdn microsoft com en us library ms633573 aspx功能 我知道我可以通过将类成员设为静态来简单地做到这一点 但是 作为一名 C 11 ST
  • 计算 Richtextbox 中所有单词的最有效方法是什么?

    我正在编写一个文本编辑器 需要提供实时字数统计 现在我正在使用这个扩展方法 public static int WordCount this string s s s TrimEnd if String IsNullOrEmpty s re
  • 在 DataView 的 RowFilter 中选择 DISTINCT

    我试图根据与另一个表的关系缩小 DataView 中的行范围 我使用的 RowFilter 如下 dv new DataView myDS myTable id IN SELECT DISTINCT parentID FROM myOthe
  • 在 C 中匹配二进制模式

    我目前正在开发一个 C 程序 需要解析一些定制的数据结构 幸运的是我知道它们是如何构造的 但是我不确定如何在 C 中实现我的解析器 每个结构的长度都是 32 位 并且每个结构都可以通过其二进制签名来识别 举个例子 有两个我感兴趣的特定结构
  • 使用 LINQ2SQL 在 ASP.NET MVC 中的各种模型存储库之间共享数据上下文

    我的应用程序中有 2 个存储库 每个存储库都有自己的数据上下文对象 最终结果是我尝试将从一个存储库检索到的对象附加到从另一个存储库检索到的对象 这会导致异常 Use 构造函数注入将 DataContext 注入每个存储库 public cl
  • 单个对象的 Monogame XNA 变换矩阵?

    我读过一些解释 XNA Monogame 变换矩阵的教程 问题是这些矩阵应用于 SpriteBatch Begin matrix 这意味着所有 Draw 代码都将被转换 如何将变换矩阵应用于单个可绘制对象 就我而言 我想转换滚动背景 使其自
  • 回发后刷新时提示确认表单重新提交。我做错了什么?

    我有一个以空白 默认状态启动的仪表板 我让用户能够将保存的状态加载到仪表板中 当他们单击 应用 按钮时 我运行以下代码 function CloseAndSave var radUpload find radUpload1ID var in
  • 在 Visual Studio 2010 中从 Fortran 调用 C++ 函数

    我想从 Fortran 调用 C 函数 为此 我在 Visual Studio 2010 中创建了一个 FORTRAN 项目 之后 我将一个 Cpp 项目添加到该 FORTRAN 项目中 当我要构建程序时出现以下错误 Error 1 unr
  • qdbusxml2cpp 未知类型

    在使用 qdbusxml2cpp 程序将以下 xml 转换为 Qt 类时 我收到此错误 qdbusxml2cpp c ObjectManager a ObjectManager ObjectManager cpp xml object ma
  • 在一个平台上,对于所有数据类型,所有数据指针的大小是否相同? [复制]

    这个问题在这里已经有答案了 Are char int long 甚至long long 大小相同 在给定平台上 不能保证它们的大小相同 尽管在我有使用经验的平台上它们通常是相同的 C 2011 在线草稿 http www open std
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的
  • 外键与独立关系 - Entity Framework 5 有改进吗?

    我读过了several http www ladislavmrnka com 2011 05 foreign key vs independent associations in ef 4 文章和问题 https stackoverflow
  • 动态添加 ASP.Net 控件

    我有一个存储过程 它根据数据库中存储的记录数返回多行 现在我想有一种方法来创建 div 带有包含该行值的控件的标记 如果从数据库返回 10 行 则 10 div 必须创建标签 我有下面的代码来从数据库中获取结果 但我不知道如何从这里继续 S
  • 如何在非控制台应用程序中查看 cout 输出?

    输出到调试窗口似乎相当繁琐 我在哪里可以找到cout如果我正在编写非控制台信息 则输出 Like double i a b cout lt lt b lt lt endl I want to check out whether b is z
  • C++ 函数重载类似转换

    我收到一个错误 指出两个重载具有相似的转换 我尝试了太多的事情 但没有任何帮助 这是那段代码 CString GetInput int numberOfInput BOOL clearBuffer FALSE UINT timeout IN
  • 调用堆栈中的“外部代码”是什么意思?

    我在 Visual Studio 中调用一个方法 并尝试通过检查调用堆栈来调试它 其中一些行标记为 外部代码 这到底是什么意思 方法来自 dll已被处决 外部代码 意味着该dll没有可用的调试信息 你能做的就是在Call Stack窗口中单
  • 方法优化 - C#

    我开发了一种方法 允许我通过参数传入表 字符串 列数组 字符串 和值数组 对象 然后使用这些参数创建参数化查询 虽然它工作得很好 但代码的长度以及多个 for 循环散发出一种代码味道 特别是我觉得我用来在列和值之间插入逗号的方法可以用不同的
  • C++ 条件编译

    我有以下代码片段 ifdef DO LOG define log p record p else define log p endif void record char data 现在如果我打电话log hello world 在我的代码中
  • WebSocket安全连接自签名证书

    目标是一个与用户电脑上安装的 C 应用程序交换信息的 Web 应用程序 客户端应用程序是 websocket 服务器 浏览器是 websocket 客户端 最后 用户浏览器中的 websocket 客户端通过 Angular 持久创建 并且
  • 如何从 ODBC 连接获取可用表的列表?

    在 Excel 中 我可以转到 数据 gt 导入外部数据 gt 导入数据 然后选择要使用的数据源 然后在提供登录信息后 它会给我一个表格列表 我想知道如何使用 C 以编程方式获取该列表 您正在查询什么类型的数据源 SQL 服务器 使用权 看

随机推荐

  • QT信号与槽的自动连接

    信号与槽的自动连接 信号与槽可以通过使用手写代码显式的实现关联 也可以运用 QMetaObject 类规定的槽 函数命名范式来实现自动关联 显式关联 首先我们来看一下 不使用 自动关联规则 的情形 在下面这段代码里面 我们定义了一个对话框类
  • 48、Flutter之widgets LayoutBuilder组件

    LayoutBuilder 通过 LayoutBuilder 我们可以在布局过程中拿到父组件传递的约束信息 然后我们可以根据约束信息动态的构建不同的布局 比如我们实现一个响应式的 Column 组件 ResponsiveColumn 它的功
  • jquery筛选器

    在Web应用程序中 大部分的客户端操作都是基于对象的操作 要操作对象就必须先获取对象 jQuery提供了强大的选择器让我们获取对象 我人为地将jQuery选择器分为两大部分 选择对象和筛选条件 选择对象表示要获取什么对象 筛选条件是对获取的
  • # 2023 好用免费图床推荐

    1 聚合图床 该免费图床由来已久 稳定运行多年 满足多种个性化需要 可以说该图床是所有推荐当中最为灵活 扩展性最强图床 支持水印设置 防盗链设置 甚至还可以统计数据量大小 有普通用户和付费用户区分 付费用户自定义功能将更加强大 就个人而言
  • Spring Cloud Config配置服务及那些你不知道的坑

    目录 1 为什么选择Spring Cloud Config 1 1 集中式管理 1 2 动态修改配置 2 Spring Cloud Config 简介 3 服务端配置 3 1 添加依赖 3 2 开启服务注册 3 3 添加YML配置 3 4
  • springboot结合aop和pagehelper实现分页筛选排序功能

    一 前言 首先我们要知道什么是aop 什么是pagehelper 从而我们知道 pagehelper的实现其实是两行代码 PageHelper startPage pageNum pageSize 开始分页 PageInfo pageInf
  • Python中class的内置函数__str__

    Python中class的内置函数 str 一 说明 init 类实例初始化函数 str 类实例字符串化函数 二 示例1 1 2 3 4 5 6 class Fri
  • Java并发编程学习2-线程安全性

    Java并发编程学习系列 线程安全性 引言 1 什么是线程安全性 1 1 如何编写线程安全的代码 1 2 线程安全类 1 3 无状态对象 2 原子性 2 1 竞态条件 2 2 延迟初始化 2 3 复合操作 3 加锁机制 3 1 内置锁 3
  • 华为OD机试 - 根据某条件聚类最少交换次数(Java)

    目录 题目描述 输入描述 输出描述 用例 题目解析 算法源码 题目描述 给出数字K 请输出所有结果小于K的整数组合到一起的最少交换次数 组合一起是指满足条件的数字相邻 不要求相邻后在数组中的位置 数据范围 100 lt K lt 100 1
  • 编写程序,生成一个包含50个随机整数的列表,然后删除奇数

    可以这样编写程序 list for i in range 50 list append random randint 1 100 for num in list if num 2 0 list remove num print list
  • latex入门学习笔记总结

    目录 latex文件的组织方式 latex中的字符 latex中的强调 latex中的分页和断行 latex中文档元素 latex的环境 列表环境 代码环境 htbp 命令 latex中的表格 latex中的图片 插入一张图片 两图并排 插
  • linux指令timedatectl,centos7设置时间命令timedatectl

    在新的centos7里 关于时间的指令除了保留了之前版本中常用到的date hwclock等命令外 还增加了一个统一的命令timedatactl 下面结合其用法进行下小结 查看 timedatectl 指令用法帮助 root 361way
  • WPF自学篇--第一篇--Hello world

    主要知识点为 1 WPF如何修改启动页面 2 如何写Hello Word Sample 内容 1 由于专案是先加window wpf想加web wpf是调试找启动页面找了很久 终于发现在app config中Application下 Sta
  • python LeetCode 88 刷题记录

    题目 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2 另有两个整数 m 和 n 分别表示 nums1 和 nums2 中的元素数目 请你 合并 nums2 到 nums1 中 使合并后的数组同样按 非递减顺序 排列 注意
  • 修改Windows系统注册表并使其立即生效

    title 修改Windows系统注册表并使其立即生效 update 2019 12 22 15 38 06 description 修改Windows系统注册表并使其立即生效的方法 原文地址https tomsworkspace gith
  • 日常生活中常用的五星级句子

    1 After you 你先请 这是一句很常用的客套话 在进 出门 上车得场合你都可以表现一下 好象现在女士不愿意你这么做 特别是那些女权主义者 我还记得这么一段话 一个女士对一个让她先行的男士说 you do this because i
  • 基础版图书管理系统(Java实现)

    文章目录 前言 一 设计书类 书架类 二 设计用户类 1 管理员 2 普通用户 三 操作包 前言 对于图书管理系统我想大家都不会陌生 在C语言的学习中相信大家都写过这个系统 那么今天我们就用Java来实现一下图书管理系统 看看和C语言又有什
  • Linux下查看目录文件数和文件大小

    一 查看当前目录下文件个数 在linux下查看目录下有多少文件可以用 ls l 命令查看 ls lR 递归查看所有目录 如果文件很多 则用wc命令 和 grep 命令进行过滤 wc命令显示输出的行 列 字符数 l表示仅列出行 w表示仅列出多
  • Typescript:类的装饰器

    Typescript的装饰器我在学习typescript的时候并不是很清楚它的作用场景 直到使用了nest js框架后 才明白其作用 于是又深入学习了一下 希望通过对装饰器的学习提高对nest js的使用 装饰器 装饰器为我们在类的声明及成
  • 常用设计模式总结

    设计模式的相关知识 很多书籍和博客中都有详细总结 本文总结的目的 1 将自己学习到的设计模式的知识按照自己的逻辑重新总结 方便查看和记忆 2 方便让自己对设计模式中常用的知识有一个系统的认知 设计模式 话设计模式 书中提到 24 种设计模式