C++ 设计模式——建造者模式

news2025/1/11 14:28:06

建造者模式

    • 建造者模式
      • 组成部分
      • 建造者模式使用步骤
        • 1. 定义产品类
        • 2. 创建具体产品类
        • 3. 创建建造者接口
        • 4. 实现具体建造者
        • 5. 创建指挥者类
        • 6. 客户端代码
      • 建造者模式 UML 图
      • 建造者模式 UML 图解析
      • 建造者模式的优缺点
      • 建造者模式的适用场景
      • 完整代码

建造者模式

建造者模式(Builder Pattern)是一种创建型设计模式,它允许使用多个简单的对象一步步构建一个复杂的对象。建造者模式通过将对象的构建过程与表示分离,使得同样的构建过程可以创建不同的表示。

引入“建造者”模式的定义:将一个复杂的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

组成部分

  • 产品(Product):代表最终构建的复杂对象,包含多个部件和属性。产品类定义了对象的结构和行为,通常包括多个属性和方法。
  • 建造者(Builder):定义创建产品的各个部件的抽象接口。建造者接口提供了构建不同部分的方法,允许灵活地创建不同类型的产品。
  • 具体建造者(ConcreteBuilder):实现建造者接口,具体构建和装配产品的各个部件。每个具体建造者负责创建特定类型的产品,同时维护构建过程的状态。
  • 指挥者(Director):负责管理建造过程,调用建造者的方法以生成产品。指挥者定义构建的顺序,确保产品的构建过程符合预期。

建造者模式使用步骤

1. 定义产品类

首先定义一个复杂对象的类 Monster,它是所有怪物的基类。

//怪物父类
class Monster
{
public:
    virtual ~Monster() {} //做父类时析构函数应该为虚函数
    // 其他方法可以在这里定义
};
2. 创建具体产品类

创建多个具体的怪物类,继承自 Monster 类。

//亡灵类怪物
class M_Undead :public Monster{};

//元素类怪物
class M_Element :public Monster{};

//机械类怪物
class M_Mechanic :public Monster{};
3. 创建建造者接口

定义一个抽象建造者类 MonsterBuilder,声明构建产品各个部分的方法。

//怪物构建器父类
class MonsterBuilder
{
public:
	virtual ~MonsterBuilder() {} //做父类时析构函数应该为虚函数
	//返回指向Monster类的成员变量指针m_pMonster,当一个复杂的对象构建完成后,可以通过该成员函数把对象返回
	Monster* GetResult()
	{
		return m_pMonster;
	}

	virtual void LoadTrunkModel(string strno) = 0;//这里也可以写为空函数体,子类决定是否重新实现
	virtual void LoadHeadModel(string strno) = 0;
	virtual void LoadLimbsModel(string strno) = 0;

protected:
	Monster* m_pMonster;  //指向Monster类的成员变量指针
};
4. 实现具体建造者

创建多个具体建造者类,负责构建不同类型的怪物。

//亡灵类怪物构建器类
class M_UndeadBuilder :public MonsterBuilder
{
public:
	M_UndeadBuilder() //构造函数
	{
		m_pMonster = new M_Undead();
	}

	virtual void LoadTrunkModel(string strno)
	{
		cout << "载入亡灵类怪物的躯干部位模型,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//具体要做的事情其实是委托给怪物子类来完成,委托指把本该自己实现的功能转给其他类实现
		//m_pMonster->......略

	}
	virtual void LoadHeadModel(string strno)
	{
		cout << "载入亡灵类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
	virtual void LoadLimbsModel(string strno)
	{
		cout << "载入亡灵类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
};

//元素类怪物构建器类
class M_ElementBuilder :public MonsterBuilder
{
public:
	M_ElementBuilder() //构造函数
	{
		m_pMonster = new M_Element();
	}

	virtual void LoadTrunkModel(string strno)
	{
		cout << "载入元素类怪物的躯干部位模型,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略			
	}
	virtual void LoadHeadModel(string strno)
	{
		cout << "载入元素类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
	virtual void LoadLimbsModel(string strno)
	{
		cout << "载入元素类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
};
//机械类怪物构建器类
class M_MechanicBuilder :public MonsterBuilder
{
public:
	M_MechanicBuilder() //构造函数
	{
		m_pMonster = new M_Mechanic();
	}

	virtual void LoadTrunkModel(string strno)
	{
		cout << "载入机械类怪物的躯干部位模型,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
	virtual void LoadHeadModel(string strno)
	{
		cout << "载入机械类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
	virtual void LoadLimbsModel(string strno)
	{
		cout << "载入机械类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
};
5. 创建指挥者类

定义一个指挥者类 MonsterDirector,负责管理建造过程。

//指挥者类
class MonsterDirector
{
public:
	MonsterDirector(MonsterBuilder* ptmpBuilder) //构造函数
	{
		m_pMonsterBuilder = ptmpBuilder;
	}

	//指定新的构建器
	void SetBuilder(MonsterBuilder* ptmpBuilder)
	{
		m_pMonsterBuilder = ptmpBuilder;
	}

	//原MonsterBuilder类中的Assemble成员函数
	Monster* Construct(string strmodelno) //参数:模型编号,形如“1253679201245”等,每些位的组合都有一些特别的含义,这里无需深究
	{
		m_pMonsterBuilder->LoadTrunkModel(strmodelno.substr(4, 3));  //载入躯干模型,截取某部分字符串以表示躯干模型的编号
		m_pMonsterBuilder->LoadHeadModel(strmodelno.substr(7, 3));   //载入头部模型并挂接到躯干模型上
		m_pMonsterBuilder->LoadLimbsModel(strmodelno.substr(10, 3)); //载入四肢模型并挂接到躯干模型上
		return m_pMonsterBuilder->GetResult();  //返回构建后的对象
	}
private:
	MonsterBuilder* m_pMonsterBuilder; //指向所有构建器类的父类
};
6. 客户端代码

在客户端中,创建具体建造者和指挥者的实例,使用指挥者构建产品。

int main() 
{
    // 创建亡灵类怪物建造者
    M_UndeadBuilder* undeadBuilder = new M_UndeadBuilder();
    MonsterDirector undeadDirector(undeadBuilder); // 创建亡灵类怪物的指挥者

    // 构建亡灵类怪物
    Monster* undeadMonster = undeadDirector.Construct("1253679201245");
    // 使用构建的亡灵类怪物对象
    // ...

    // 创建元素类怪物的建造者
    M_ElementBuilder* elementBuilder = new M_ElementBuilder();
    MonsterDirector elementDirector(elementBuilder); // 创建元素类怪物的指挥者

    // 构建元素类怪物
    Monster* elementMonster = elementDirector.Construct("1253679201245");
    // 使用构建的元素类怪物对象
    // ...

    // 创建机械类怪物的建造者
    M_MechanicBuilder* mechanicBuilder = new M_MechanicBuilder();
    MonsterDirector mechanicDirector(mechanicBuilder); // 创建机械类怪物的指挥者

    // 构建机械类怪物
    Monster* mechanicMonster = mechanicDirector.Construct("1253679201245");
    // 使用构建的机械类怪物对象
    // ...

    // 清理资源
    delete undeadMonster; // 释放亡灵类怪物对象
    delete elementMonster; // 释放元素类怪物对象
    delete mechanicMonster; // 释放机械类怪物对象
    delete undeadBuilder; // 释放亡灵类建造者对象
    delete elementBuilder; // 释放元素类建造者对象
    delete mechanicBuilder; // 释放机械类建造者对象

    return 0;
}

建造者模式 UML 图

建造者模式 UML 图

建造者模式 UML 图解析

  • Builder (抽象构建器)
    • MonsterBuilder 类定义了创建产品对象的各个部件的抽象接口,包括方法如 LoadTrunkModelLoadHeadModelLoadLimbsModel。同时,它还定义了 GetResult 接口,用于返回所创建的复杂对象。
  • ConcreteBuilder (具体构建器)
    • 具体构建器类(如 M_UndeadBuilderM_ElementBuilderM_MechanicBuilder)实现了 MonsterBuilder 接口,负责具体的构造和装配过程。每个具体构建器定义了其所创建的复杂对象(如 M_UndeadM_ElementM_Mechanic),并可能提供方法返回创建好的复杂对象。
  • Product (产品)
    • 产品类指被构建的复杂对象,包含多个部件。具体构建器负责创建该产品的内部表示并定义它的装配过程。例如,M_UndeadM_ElementM_Mechanic 类分别表示不同类型的怪物。
  • Director (指挥者)
    • MonsterDirector 类是指挥者,持有一个指向抽象构建器的引用(如 m_pMonsterBuilder)。它利用该引用在 Construct 成员函数中调用构建器对象的方法,完成复杂对象的构建。指挥者控制构建过程的顺序,确保各个部件按正确的顺序装配。例如,Construct 函数中会依次调用 LoadTrunkModelLoadHeadModelLoadLimbsModel
  • 客户端
    • 在客户端(如 main 函数)中,用户只需生成一个具体的构建器对象,并利用该构建器对象创建指挥者对象,调用指挥者的 Construct 方法,就可以构建出所需的复杂对象。这种设计使得客户端与具体实现解耦,提高了灵活性和可维护性。

建造者模式的优缺点

优点

  • 解耦:建造者模式将复杂对象的构建与表示分离,使得构建过程与产品的表示相互独立,降低了模块间的耦合度。
  • 灵活性:可以通过更换具体建造者来灵活地构建不同类型的产品,而无需修改指挥者或客户端代码。
  • 可读性和可维护性:通过清晰的接口和结构,增加了代码的可读性,便于后续的维护和扩展。
  • 支持复杂对象的构建:适合构建复杂对象,特别是那些具有多个部件和装配顺序要求的对象。
  • 步骤控制:指挥者可以控制构建的步骤和顺序,确保复杂对象按照预期的方式构建。

缺点

  • 复杂性:对于简单对象的构建,使用建造者模式可能显得过于复杂,增加了不必要的代码和结构。
  • 类的数量增加:需要创建多个具体建造者类和产品类,可能导致类的数量增加,增加了系统的复杂性。
  • 构建过程固定:一旦定义了指挥者的构建过程,可能不够灵活,难以适应一些动态变化的需求。

建造者模式的适用场景

  • 构建复杂对象:当对象由多个部件组成,且构建和组合过程复杂时,例如游戏中的角色(如怪物、NPC)。
  • 产品变体多样化:当需要创建多种不同类型的产品,但构建过程相似时,例如不同类型的车辆(轿车、卡车、摩托车)。
  • 构建过程需要控制:当构建过程需要明确的步骤和顺序时,例如根据不同条件动态调整构建顺序。
  • 对象的构建与表示分离:当产品的构建与表示需要解耦,以便于后续维护和扩展,例如构建复杂文档时。
  • 逐步构建对象:当对象的构建过程需要分步进行,或在构建过程中进行条件判断时,例如配置复杂系统。
  • 需要灵活的对象创建:当需要在运行时决定构建哪种类型的对象时,例如根据用户输入或配置文件动态选择不同的构建器。

完整代码

#include <iostream>
#include <string>

using namespace std;

//怪物父类
class Monster
{
public:
	virtual ~Monster() {} //做父类时析构函数应该为虚函数
	//void Assemble(string strmodelno) //参数:模型编号,形如“1253679201245”等,每些位的组合都有一些特别的含义,这里无需深究
	//{
	//	LoadTrunkModel(strmodelno.substr(4, 3));  //载入躯干模型,截取某部分字符串以表示躯干模型的编号
	//	LoadHeadModel(strmodelno.substr(7, 3));   //载入头部模型并挂接到躯干模型上
	//	LoadLimbsModel(strmodelno.substr(10, 3)); //载入四肢模型并挂接到躯干模型上
	//}

	//virtual void LoadTrunkModel(string strno) = 0; //这里也可以写为空函数体,子类决定是否重新实现
	//virtual void LoadHeadModel(string strno) = 0;
	//virtual void LoadLimbsModel(string strno) = 0;
};

//亡灵类怪物
class M_Undead :public Monster
{
	/*public:
		virtual void LoadTrunkModel(string strno)
		{
			cout << "载入亡灵类怪物的躯干部位模型,需要调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		}
		virtual void LoadHeadModel(string strno)
		{
			cout << "载入亡灵类怪物的头部模型并挂接到躯干部位,需要调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		}
		virtual void LoadLimbsModel(string strno)
		{
			cout << "载入亡灵类怪物的四肢模型并挂接到躯干部位,需要调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		}*/
};

//元素类怪物
class M_Element :public Monster
{
	/*public:
		virtual void LoadTrunkModel(string strno)
		{
			cout << "载入元素类怪物的躯干部位模型,需要调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		}
		virtual void LoadHeadModel(string strno)
		{
			cout << "载入元素类怪物的头部模型并挂接到躯干部位,需要调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		}
		virtual void LoadLimbsModel(string strno)
		{
			cout << "载入元素类怪物的四肢模型并挂接到躯干部位,需要调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		}*/
};

//机械类怪物
class M_Mechanic :public Monster
{
	/*public:
		virtual void LoadTrunkModel(string strno)
		{
			cout << "载入机械类怪物的躯干部位模型,需要调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		}
		virtual void LoadHeadModel(string strno)
		{
			cout << "载入机械类怪物的头部模型并挂接到躯干部位,需要调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		}
		virtual void LoadLimbsModel(string strno)
		{
			cout << "载入机械类怪物的四肢模型并挂接到躯干部位,需要调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		}*/
};


//-----------------
//怪物构建器父类
class MonsterBuilder
{
public:
	virtual ~MonsterBuilder() {} //做父类时析构函数应该为虚函数
	//void Assemble(string strmodelno) //参数:模型编号,形如“1253679201245”等,每些位的组合都有一些特别的含义,这里无需深究
	//{
	//	LoadTrunkModel(strmodelno.substr(4, 3));  //载入躯干模型,截取某部分字符串以表示躯干模型的编号
	//	LoadHeadModel(strmodelno.substr(7, 3));   //载入头部模型并挂接到躯干模型上
	//	LoadLimbsModel(strmodelno.substr(10, 3)); //载入四肢模型并挂接到躯干模型上
	//}
	//返回指向Monster类的成员变量指针m_pMonster,当一个复杂的对象构建完成后,可以通过该成员函数把对象返回
	Monster* GetResult()
	{
		return m_pMonster;
	}

	virtual void LoadTrunkModel(string strno) = 0;//这里也可以写为空函数体,子类决定是否重新实现
	virtual void LoadHeadModel(string strno) = 0;
	virtual void LoadLimbsModel(string strno) = 0;

protected:
	Monster* m_pMonster;  //指向Monster类的成员变量指针
};

//---------------
//亡灵类怪物构建器类
class M_UndeadBuilder :public MonsterBuilder
{
public:
	M_UndeadBuilder() //构造函数
	{
		m_pMonster = new M_Undead();
	}

	virtual void LoadTrunkModel(string strno)
	{
		cout << "载入亡灵类怪物的躯干部位模型,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//具体要做的事情其实是委托给怪物子类来完成,委托指把本该自己实现的功能转给其他类实现
		//m_pMonster->......略

	}
	virtual void LoadHeadModel(string strno)
	{
		cout << "载入亡灵类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
	virtual void LoadLimbsModel(string strno)
	{
		cout << "载入亡灵类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
};

//元素类怪物构建器类
class M_ElementBuilder :public MonsterBuilder
{
public:
	M_ElementBuilder() //构造函数
	{
		m_pMonster = new M_Element();
	}

	virtual void LoadTrunkModel(string strno)
	{
		cout << "载入元素类怪物的躯干部位模型,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略			
	}
	virtual void LoadHeadModel(string strno)
	{
		cout << "载入元素类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
	virtual void LoadLimbsModel(string strno)
	{
		cout << "载入元素类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
};
//机械类怪物构建器类
class M_MechanicBuilder :public MonsterBuilder
{
public:
	M_MechanicBuilder() //构造函数
	{
		m_pMonster = new M_Mechanic();
	}

	virtual void LoadTrunkModel(string strno)
	{
		cout << "载入机械类怪物的躯干部位模型,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
	virtual void LoadHeadModel(string strno)
	{
		cout << "载入机械类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
	virtual void LoadLimbsModel(string strno)
	{
		cout << "载入机械类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;
		//m_pMonster->......略
	}
};

//指挥者类
class MonsterDirector
{
public:
	MonsterDirector(MonsterBuilder* ptmpBuilder) //构造函数
	{
		m_pMonsterBuilder = ptmpBuilder;
	}

	//指定新的构建器
	void SetBuilder(MonsterBuilder* ptmpBuilder)
	{
		m_pMonsterBuilder = ptmpBuilder;
	}

	//原MonsterBuilder类中的Assemble成员函数
	Monster* Construct(string strmodelno) //参数:模型编号,形如“1253679201245”等,每些位的组合都有一些特别的含义,这里无需深究
	{
		m_pMonsterBuilder->LoadTrunkModel(strmodelno.substr(4, 3));  //载入躯干模型,截取某部分字符串以表示躯干模型的编号
		m_pMonsterBuilder->LoadHeadModel(strmodelno.substr(7, 3));   //载入头部模型并挂接到躯干模型上
		m_pMonsterBuilder->LoadLimbsModel(strmodelno.substr(10, 3)); //载入四肢模型并挂接到躯干模型上
		return m_pMonsterBuilder->GetResult();  //返回构建后的对象
	}
private:
	MonsterBuilder* m_pMonsterBuilder; //指向所有构建器类的父类
};

int main()
{
	// 创建具体建造者
	M_UndeadBuilder* undeadBuilder = new M_UndeadBuilder();
	MonsterDirector undeadDirector(undeadBuilder); // 创建亡灵类怪物的指挥者

	// 构建亡灵类怪物
	Monster* undeadMonster = undeadDirector.Construct("1253679201245");
	// 使用构建的亡灵类怪物对象
	// ...

	// 创建元素类怪物的建造者
	M_ElementBuilder* elementBuilder = new M_ElementBuilder();
	MonsterDirector elementDirector(elementBuilder); // 创建元素类怪物的指挥者

	// 构建元素类怪物
	Monster* elementMonster = elementDirector.Construct("1253679201245");
	// 使用构建的元素类怪物对象
	// ...

	// 创建机械类怪物的建造者
	M_MechanicBuilder* mechanicBuilder = new M_MechanicBuilder();
	MonsterDirector mechanicDirector(mechanicBuilder); // 创建机械类怪物的指挥者

	// 构建机械类怪物
	Monster* mechanicMonster = mechanicDirector.Construct("1253679201245");
	// 使用构建的机械类怪物对象
	// ...

	// 清理资源
	delete undeadMonster; // 释放亡灵类怪物对象
	delete elementMonster; // 释放元素类怪物对象
	delete mechanicMonster; // 释放机械类怪物对象
	delete undeadBuilder; // 释放亡灵类建造者对象
	delete elementBuilder; // 释放元素类建造者对象
	delete mechanicBuilder; // 释放机械类建造者对象

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2051462.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hogan 阻抗控制的理解

机器人阻抗控制是一种基于力的控制方法,它通过调节机器人在受到外部力作用时所表现出的抵抗能力(即阻抗),来实现与环境的良好交互。以下是对机器人阻抗控制的详细理解: 一、阻抗控制的基本原理 阻抗控制的核心思想是通过模拟物体的力学特性(如刚度、阻尼和质量),使机…

定时器处理按键抖动

一、按键的抖动 1.按键的定义和原因 按键抖动是由于‌机械按键在闭合和断开时&#xff0c;由于触点的弹性作用&#xff0c;会产生一系列的抖动现象。这种抖动对于人类来说几乎感觉不到&#xff0c;但对‌单片机来说&#xff0c;却是一个可以感应到的过程&#xff0c;且处理时…

SQL - 设计数据库

数据建模 数据建模就是为要存储在数据库中的数据创建模型的过程 步骤 1.理解和分析业务需求 (收集信息) 收集需求&#xff0c;明确业务流程&#xff0c;定义数据需求&#xff0c;分析业务规则 2.构建业务的概念模型 (识别和表示业务中实体、事务或概念以及它们之间的关系) 识别…

APP支付宝授权获取code uniapp

1.点击使用plus.runtime跳转打开支付宝 //打开支付宝授权&#xff0c;在支付宝APP中授权后会在支付宝中跳转到你填写的h5地址//urls是授权地址可以后端拼接也可以前端写死 //以下是一个拼接示例&#xff0c;需修改app_id的值和redirect_uri的值即可 //app_id是商户的APPID&…

WorkPlus-为用户提供IM即时通讯和实时音视频通信本地化服务

WorkPlus作为一家领先的企业级通讯解决方案提供商&#xff0c;为用户提供了本地化服务&#xff0c;以满足IM即时通讯和实时音视频通信的需求。本文将深入探讨WorkPlus本地化服务的重要性以及其为用户提供的IM即时通讯和实时音视频通信的解决方案。 一、本地化服务的意义 低延迟…

【动态规划、dp】[CSP-J 2022] 上升点列 题解

题目描述 在一个二维平面内&#xff0c;给定 n n n 个整数点 ( x i , y i ) (x_i, y_i) (xi​,yi​)&#xff0c;此外你还可以自由添加 k k k 个整数点。 你在自由添加 k k k 个点后&#xff0c;还需要从 n k n k nk 个点中选出若干个整数点并组成一个序列&#xff0c…

从0开始搭建vue + flask 旅游景点数据分析系统(十三)vue + flask 图片上传、用户头像更改

项目是基于我的博文&#xff1a;vue flask 旅游景点数据分析系统 基础上做的&#xff0c;可以参考之前的博客文章。 1 前端修改 主要是修改Profile.vue <!-- 头像上传 --><el-form-item label"头像"><el-uploadclass""action"/api/…

使用 Fyne 构建 GUI 应用:设置标签文本和自增计数器

引言 Fyne 是一个用 Go 语言编写的跨平台 GUI 框架&#xff0c;它提供了一套丰富的组件来帮助开发者快速构建出漂亮的用户界面。在本文中&#xff0c;我们将通过一个简单的案例来演示如何使用 Fyne 创建 GUI 应用程序&#xff0c;该程序包含设置标签文本和自增计数器的功能。 …

按钮(Buttons)-Qt-思维导图-学习笔记

按钮(Buttons) 按钮在 Qt 中的重要性 按钮是 Qt 中最常用的控件之一 通过点击按钮&#xff0c;可以响应事件&#xff0c;实现人机交互效果 按钮在嵌入式系统和 PC 端的界面交互中都是不可或缺的 Qt 内置的六种按钮部件 QPushButton&#xff1a;下压按钮 用法示例 项目创建与…

指针详解(三)

目录 1. 数组名 2. 使用指针访问数组 3. 一维数组传参的本质 4. 冒泡排序 5. 二级指针 6. 指针数组 7. 指针数组模拟二维数组 1. 数组名 在使用指针访问数组的内容时&#xff0c;有这样的代码&#xff1a; int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p &arr[0];/…

CV党福音:YOLOv8实现实例分割(二)之训练过程

在上一篇博客中&#xff0c;我们已经了解了YOLOv8实例分割的基本流程&#xff0c;本章则是对数据集、以及训练过程等进行进一步的学习。 文章目录 训练整体流程语义分割与实例分割数据集开启训练训练Debug数据封装损失函数初始化前向传播 总结 训练整体流程 训练模型的整体流…

洗衣机洗衣服一些知识

01智能:按衣物多少自动调节合适水位的标准洗涤程序 (需要30分钟时间) 02:大物:较大,较厚的衣服洗涤 03:轻柔:毛织品或内衣洗涤 04:快速:少量清污衣服洗涤 (13分钟) 05:浸泡:先浸泡一段时间再洗涤 06:单洗:只洗衣不脱水 07:单脱:只脱水不洗衣 08:洁桶:清洁洗衣桶 准备工作: (1)…

XSS反射型和DOM型+DOM破坏

目录 第一关 源码分析 payload 第二关 源码分析 payload 第三关 源码分析 payload 第四关 源码分析 payload 第五关 源码分析 payload 第六关 源码分析 第七关 源码分析 方法一&#xff1a;构造函数 方法二&#xff1a;parseInt 方法三&#xff1a;locat…

龙门吊(天车)防撞方案

防撞雷达设备&#xff0c;是一款基于无线微波技术自主研发的应答式高精度无线防撞产品&#xff0c;该产品具有测距精度高&#xff08;最高可到10厘米&#xff09;&#xff0c;测距稳定&#xff0c;无累计误差&#xff0c;粉尘、水汽不影响测距精度&#xff0c;抗电磁干扰等特点…

oracle数据库目录及文件

oracle数据库目录及文件 oracle安装后所有根目录 1、admin 目录 里边有不同文件夹&#xff0c;代表一个实例&#xff0c;记录 Oracle 实例的配置&#xff0c;运行日志等文件。每个实例一个目录。 SID&#xff1a;System IDentifier 的缩写&#xff0c;是 Oracle 实例的唯一标记…

IT服务标准化知识体系攻略(至简)

标准是为了在一定范围内获得最佳秩序 &#xff0c;经协商一致制定并由公开机构批准共同使用和重复使用的和中规范性文件。标准是标准化活动的主要成果之一。国家标准的制定有一套正常程序&#xff0c;分为预阶段、立项阶段、起草阶段、征求意见阶段、审查阶段、批准阶段、出版阶…

【区块链+金融服务】第一创业证券开发银行间报价 Dapp | FISCO BCOS应用案例

在银行间市场现券交易的过程中&#xff0c;通过银保监会发牌的代理机构进行报价交易&#xff0c;已解决无代理阶段存在的许多问题。 但是由于业务需要&#xff0c;使用以前模式进行报价交易的仍占有一定比例。 针对这一现状&#xff0c;第一创业证券基于 FISCO BCOS 区块链底层…

工作 sql 数据库创建 表的修改 插入数据

一. 创建数据库 创建数据库 CREATE DATABASE (IF NOT EXISTS) 数据库名称;使用数据库 USE 数据库名称;查看当前数据库中存在的表 SHOW TABLES;删除数据库 DROP DATABASE demolibang 二. 创建表 格式&#xff1a; CREATE TABLE IF NOT EXISTS 表名&#xff08; 字段名 字…

Unity动画模块 之 3D模型导入基础设置 Materials

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正 还是那句话&#xff0c;用到的时候再看看&#xff0c;死记硬背不是正经的学习方法&#xff0c;但是又不得不知道一下&…

javaweb_10:XML映射文件

一、规范 1、XML映射文件的名称与Mapper接口名称一致&#xff0c;并且将XML映射文件和Mapper接口放在相同的包下&#xff08;同包同名&#xff09;。 2、XML映射文件的namesapce属性为Mapper接口全限定名一致。 3、 XML映射文件中sql语句的id与Mapper接口中的方法名一致&a…