单例模式(懒汉式)弃
工作场景:配置文件
比如redis的配置文件redis.conf里面包涵连接数据库需要的连接信息、用户名、用户密码等配置信息,每个用户都有这个可修改的配置文件进行数据库连接个性化配置(这份文件不能写死但大体内容类似),所以需要使用设计模式思想进行代码复用,这里采将会用设计模式里的单例模式
补充:后续的代码是使用同一份配置来读取数据的场景,与上述描述有所出入,不过单例只有一个对象来控制资源的思想是通用的。
数据库配置文件复用写法(简化版)
#include <iostream>
//给用户一个config.txt文件,由用户自己修改文件需要个性化的值,只需要在当前代码中调入完成数据库的初始化配置
class SqlQuery
{
public:
//在构造类的时候传入连接信息,用户名,密码
SqlQuery(const std::string& conn, const std::string& username, const std::string& password)
{
m_conn = conn;
m_username = username;
m_password = password;
}
//一个查询数据库操作的成员函数
int query()
{
// 假装这里有实现。
return 0;
}
std::string m_conn;
std::string m_username;
std::string m_password;
};
//打开文件流的必要头文件
#include <fstream>
#include <iostream>
int main()
{
for (int i = 0; i < 100; i++) {
//数据库信息默认值
std::string conn = "mysql://localhost:3306/db/";
std::string username = "user";
std::string password = "password";
//一行一行的读取给用户个性化配置的文件的内容
std::fstream fs("D://Config/config.txt");
//读取数据的缓冲区
char tempStr[1024];
//当前读取数据的行数
int index = 0;
while (fs.getline(tempStr, 1024)) {
if (index == 0) {
conn = tempStr;
}
else if (index == 1) {
username = tempStr;
}
else if (index == 2) {
password = tempStr;
}
//循环读取每次加一行,直到读完
index++;
}
//打印数据
printf("conn: %s\n", conn.c_str());
printf("username: %s\n", username.c_str());
printf("password: %s\n", password.c_str());
//构建对象传入参数
SqlQuery query(conn, username, password);
// 查询操作
query.query();
}
std::cout << "C3_1\n";
}
上述传统文件配置是一种可行的方案,但存在缺点:同一用户每次连接相同的数据库的时候都需要从文件(磁盘中)中读取个性化的数据库配置文件,其实只需要保存一次或者从内存读取,否则数据库连接效率会大大降低
数据库配置文件复用写法(优化1)
//在读文件前判断是否为空,防止多次读取(优化1)
if(coon.empty()||username.empty||password.empty){
//一行一行的读取给用户个性化配置的文件的内容
std::fstream fs("D://Config/config.txt");
//读取数据的缓冲区
char tempStr[1024];
//当前读取数据的行数
int index = 0;
while (fs.getline(tempStr, 1024)) {
if (index == 0) {
conn = tempStr;
}
else if (index == 1) {
username = tempStr;
}
else if (index == 2) {
password = tempStr;
}
//循环读取每次加一行,直到读完
index++;
}
}
//使用全局静态变量防止变量作用域结束失去作用(优化2)
static std::string m_conn;
static std::string m_username;
static std::string m_password;
懒汉模式
只有在被静态getinstance调用的时候才创建对象初始化资源,是被动的,所以叫懒汉式
懒汉单例模式
//DBConfig.h 导入头文件
#ifndef CLIONWORK_DBCONFIG_H
#define CLIONWORK_DBCONFIG_H
#pragma once
#include <string>
class DBConfig
{
public:
//管理三个成员的指针
static DBConfig* config;
//获取指针的方法
static DBConfig* getInstance();
//添加获取这三个成员变量的成员函数
DBConfig();
//构造函数用实现用户配置文件参数传递
std::string getConn();
std::string getUsername();
std::string getPassword();
//私有成员变量更加符合面向对象
private:
std::string conn;
std::string username;
std::string password;
};
#endif //CLIONWORK_DBCONFIG_H
//DDBConfig.cpp
#include "DBConfig.h"
#include <fstream>
#include <iostream>
//控制一个类的指针即可管理三个成员变量
DBConfig* DBConfig::config = nullptr;
DBConfig* DBConfig::getInstance()
{
//如果指针不为空就创建类指针
if (config == nullptr) {
config = new DBConfig();
}
return config;
}
DBConfig::DBConfig()
{
// Read Config
std::fstream fs("D://Config/config.txt");
char tempStr[1024];
int index = 0;
while (fs.getline(tempStr, 1024)) {
if (index == 0) {
conn = tempStr;
}
else if (index == 1) {
username = tempStr;
}
else if (index == 2) {
password = tempStr;
}
index++;
}
//打印log用来判断读取文件时的操作
printf("Read config from file\n");
}
//在源文件中加入作用域使用
std::string DBConfig::getConn()
{
return conn;
}
std::string DBConfig::getUsername()
{
return username;
}
std::string DBConfig::getPassword()
{
return password;
}
//C3.2.cpp
#include <iostream>
class SqlQuery
{
public:
SqlQuery(const std::string& conn, const std::string& username, const std::string& password)
{
m_conn = conn;
m_username = username;
m_password = password;
}
int query()
{
// 假装这里有实现。
return 0;
}
};
#include "DBConfig.h"
int main()
{ //循环读取操作
for (int i = 0; i < 100; i++) {
DBConfig* config = DBConfig::getInstance();
SqlQuery query(config->getConn(), config->getUsername(), config->getPassword());
// 查询操作
query.query();
printf("Query : %d\n", i);
}
std::cout << "C3_1\n";
}
上述代码讲解
- 如果按简化版和优化1的代码来使用这个数据库配置文件,那么如果需要在其他文件导入文件,
- 每个文件都需要判空多次浪费资源(判空集成到一个文件里)
- 代码其他部分冗余(如果其他文件只需要这个文件的配置逻辑)
- 频繁的创建对象浪费资源(只有一个对象(单例),来管理资源)
- 在构造函数中执行代码逻辑
- C3_2.cpp(主要流程梳理)
- 这个源文件主要是执行数据库查询逻辑,而其他两个文件是执行数据库配置文件的逻辑
- main函数循环模拟100进行查询数据库的业务操作
- 先通过DBConfig里的获取静态成员指针DBConfig* DBConfig(管理三个配置文件信息成员的)的成员函数getInstance获取一个实例化类对象指针并且返回(这里做判空操作不需要在其他文件进行多次判空导致浪费资源,并且保证只有一个管理成员的类对象指针(实例单例))
返回类对象指针的时候触发DBConfig里的构造函数将配置文件里的值赋值给了当前变量,然后由查询函数调用指针获取变量,最后进行查询操作。
具体细节现在看也看不懂,暂时不学
单例作用以及缺点(下图简单总结)
资源共享:当多个对象需要共享同一个资源时,可以使用单例模式来管理该资源。例如,数据库连接池、线程池等。
配置信息:单例模式可以用于管理全局的配置信息,确保在整个应用程序中只有一个配置对象,方便访问和修改配置。
日志记录器:在日志记录中,使用单例模式可以确保只有一个日志记录器实例,所有的日志信息都在同一个地方进行记录,并且可以方便地访问和控制日志记录器。
缺点:单例模式虽然能够提供全局唯一的实例对象,但也有一些潜在的问题。例如,单例对象的生命周期很长,可能会导致资源持有过久;单例对象的状态共享可能引起并发访问的问题


浙公网安备 33010602011771号