c++ 设计模式 的课本范例(上)

news2024/11/25 12:52:17

( 0 ) 这里补充面向对象设计的几个原则:
开闭原则 OCP : 面向增补开放,面向代码修改关闭。其实反映到代码设计上就是类的继承,通过继承与多态,可以不修改原代码,又增加新的类似的功能。
依赖倒置原则 Dependency Inversion Principle DIP : 面向父类的虚函数编程,可以节省代码量与减少重复。
最少知识原则 Least Knowledge Principle LKP ; 一个类对另一个类了解的越少越好,降低耦合度。由访问接口进行连接。
单一职责原则 Single Responsibility Principle SRP : 一个类的职责应该单一,只对外提供一种功能。

(1) 框架设计模式 model mode : 算法的框架不变,算法的细节可以改变。主要依赖多态。

class Player
{
protected:
	int life;
	int magic;
	int attack;

	virtual void effect_self() {}
	virtual void effect_enemy() {}
	virtual bool can_burn() = 0;
public:
	Player(int life, int magic, int attack) : life(life), magic(magic), attack(attack) {}
	virtual Player(){}

	void play_effect_burn() { cout << "play_effect_burn\n"; }

	void burn()    // 模板模式:算法的框架不变,细节可以变
	{
		if (can_burn())
		{
			effect_enemy();
			effect_self();
			play_effect_burn();
		}
	}
};

class Fighter : public Player
{
public:
	Fighter() : Player(100, 100, 50) {}

	void effect_self() override { this->life -= 30; }
	void effect_enemy() override { cout << "敌人被燃烧 40 血\n"; }
	bool can_burn() override 
	{ 
		if (this->life >= 40)  return true;
		else                   return false; 
	}
};

(2)简单工厂模式:不要直接使用 new A() 创建对象,一旦对类 A 增删和修改参数,会紧耦合,牵一发动全身,用一个函数集中使用 new A ,返回创建好的对象,如同工厂批量生产产品一样。对构造对象时 的修改也限制在工厂方法里。

class Player  // 角色父类
{
protected:
	int life;
	int magic;
	int attack;

public:
	Player(int life, int magic, int attack) : life(life), magic(magic), attack(attack) {}
	virtual Player(){}
};

class Fighter : public Player   // 战士
{
public:
	Fighter() : Player(100, 100, 50) {}
};

class Master : public Player  // 法师
{
public:
	Master() : Player(50, 300, 150) {}
};

class Create   // 把 new 语句集中在产生对象的函数里,减小代码以后升级时需要修改的范围
{
public:
	static Player* createPlayer(string str)
	{
		if(str == "fighter") 
			return new Fighter();
		else if(str == "master")
			return new Master();
	}	
};

int main()
{
	auto pFighter = Create::createPlayer("fighter");
	auto pMaster = Create::createPlayer("master");

	delete pFighter;
	delete pMaster;
	return 0;
}

但工厂函数里的 if 选择,如果要创建新类,会修改原代码。面向对象的 OCP 原则:更新代码时,尽量追加新代码,而不是修改原代码,向增加开放,向修改关闭。如此引出工厂模式。

(3) 工厂模式:符合 OCP 规则的 用工厂方式生产对象:

class Player  // 角色父类
{
protected:
	int life;
	int magic;
	int attack;

public:	Player(int life, int magic, int attack) : life(life), magic(magic), attack(attack) {}
	virtual Player(){}
};

class Fighter : public Player   // 战士
{
public:	Fighter(int life, int magic, int attack) : Player(life , magic , attack) {}
};

class Master : public Player  // 法师
{
public:	Master(int life, int magic, int attack) : Player(life, magic, attack) {}
};

class Create   // 制造生产对象的虚基类
{
public:	virtual Player* createPlayer() = 0;
	virtual Create(){}
};

class Create_Fighter : public Create  // 对应对象的工厂类
{
public:	Player* createPlayer() override { return new Fighter(50,50,50); }
};

class Create_Monster : public Create
{
public:	Player* createPlayer() override { return new Master(60,60,60); }
};

int main()
{
	auto pFactFight = new Create_Fighter();
	auto pFighter = pFactFight->createPlayer();

	auto pFactMaster = new Create_Monster();
	auto pMastr = pFactMaster->createPlayer();

	delete pFactFight;
	delete pFactMaster;
	delete pFighter;
	delete pMastr;
	return 0;
}

(4) 抽象工厂模式,比工厂模式密度更高的生产对象的模式:一个工厂类包含了多个生产对象的函数:

class Player  // 角色父类
{
protected:
	int life;
	int magic;
	int attack;

public:	Player(int life, int magic, int attack) : life(life), magic(magic), attack(attack) {}
	virtual Player(){}
};

class Fighter_Land : public Player   // 陆上战士 
{
public:	Fighter_Land(int life, int magic, int attack) : Player(life , magic , attack) {}
};

class Fighter_Sea : public Player   // 海洋战士
{
public:	Fighter_Sea(int life, int magic, int attack) : Player(life, magic, attack) {}
};

class Master_Land : public Player  // 陆上法师 ,游戏新版本,不同的游戏场景
{
public:	Master_Land (int life, int magic, int attack) : Player(life, magic, attack) {}
};

class Master_Sea : public Player  // 海洋法师
{
public:	Master_Sea(int life, int magic, int attack) : Player(life, magic, attack) {}
};

class Create   // 制造生产对象的虚基类
{
public:	
	virtual Player* createPlayer() = 0;
	virtual Player* createMaster() = 0;
	virtual Create(){}
};

class Create_Land : public Create  // 不同场景下的角色生产工厂
{
public:
	Player* createPlayer() override { return new Fighter_Land(10,10,10); }
	Player* createMaster() override { return new Master_Land(20,20,20); }
};

class Create_Sea : public Create
{
public:
	Player* createPlayer() override { return new Fighter_Sea(10,10,10); }
	Player* createMaster() override { return new Master_Sea(20, 20, 20); }
};

int main()
{
	auto pCreate_Land = new Create_Land();
	auto pFighter_Land = pCreate_Land->createPlayer();;
	auto PMaster_Land = pCreate_Land->createMaster();

	auto pCreate_Sea = new Create_Sea();
	auto pFighter_Sea = pCreate_Sea->createPlayer();
	auto pMaster_Sea = pCreate_Sea->createMaster();

	delete pCreate_Land; delete pFighter_Land; delete PMaster_Land;
	delete pCreate_Sea;  delete pFighter_Sea;  delete pMaster_Sea;

	return 0;
}

工厂模式不要忘记 delete 这些指针,包括在堆区创建的工厂对象和工厂生产的类对象,都要在最后 delete 掉,释放掉。

(5) 原型模式:通过对象原型来产生新的对象。主要是把工厂类里生产对象的方法转移到了对象类里。 clone() 函数

class Player  // 角色父类
{
protected:
	int life;
	int magic;
	int attack;

public:	
	Player(int life, int magic, int attack) : life(life), magic(magic), attack(attack) {}
	Player(const Player& p) :life(p.life), magic(p.magic), attack(p.attack) {}
	~Player() {}    // 父类应有的虚析构函数

	virtual Player* clone() = 0;
};

class Fighter_Land : public Player   // 陆上战士 
{
public:	
	Fighter_Land(int life, int magic, int attack) : Player(life , magic , attack) {}
	Fighter_Land(const Fighter_Land& p) : Player(p) {}

	Player* clone() override { return new Fighter_Land(* this); }
};

(6) 建造者模式( builder ):与工厂模式的区别是:工厂模式生产的对象简单,可以直接交付。若生产的对象复杂,比如还要组装游戏角色,加工后再返回对象 ; 或者把前端页面里的报表(有头部,主体和尾部)组合转换为 txt 、 xmL、json 格式交给后端处理。只有经过对对象的加工处理,才可以得到复杂的对象,就是建造者模式,建造二字突出其是要创建复杂对象,突出建造的复杂性。而且可以把加工建造对象的过程单独拎出来实现批量建造。

class Player  // 角色父类
{
protected:
	int life;
	int magic;
	int attack;
public:
	virtual ~Player() {}    // 父类应有的虚析构函数
	Player(int life, int magic, int attack) : life(life), magic(magic), attack(attack) {}
};

class Fighter : public Player   // 战士 
{
public:
	Fighter(int life, int magic, int attack) : Player(life, magic, attack) {}
};

class Create   // 制造生产对象的虚基类
{
protected:
	Player* ptrPlayer;
public:
	virtual ~Create() { if (ptrPlayer) delete ptrPlayer; }  // 这里析构函数要释放指针

	Player* getPlayer() { return std::exchange(ptrPlayer, nullptr); }

	virtual void loadHead() = 0;
	virtual void loadTrunk() = 0;
	virtual void loadFeet() = 0;
};

class Create_Fight : public Create  // 制造战士的工厂
{
public:
	Create_Fight() :Create() { ptrPlayer = new Fighter(10, 10, 10); }
	void loadHead()  override { cout << "load head\n"; }
	void loadTrunk() override { cout << "load trunk\n"; }
	void loadFeet()  override { cout << "load feet\n"; }
};

class Assemble
{
private:
	Create* ptrCreate;
public:
	Assemble(Create* t) : ptrCreate(t) {}
	Player* assemble()
	{
		ptrCreate->loadHead();
		ptrCreate->loadTrunk();
		ptrCreate->loadFeet();

		return ptrCreate->getPlayer();
	}
};

int main()
{
	auto pFighterCreate = new Create_Fight();
	auto pAssemble = new Assemble(pFighterCreate);
	auto pFight = pAssemble->assemble();

	delete pFighterCreate;  // 经测试没有内存泄露
	delete pAssemble;
	delete pFight;

	return 0;
}

本模式另举一例:根据前端页面的报表来构造完整的数据报表,供后端使用:

在这里插入图片描述

相关代码如下:

class Head  // 日报头
{
private:
	string department;
	string date;
public:
	Head(const string& dep, const string& time) : department(dep), date(time) {}
	string& getDepart() { return department; }
	string& getDate()   { return date; }
};

class Content  // 日报内容
{
private:
	string content;
	double totalTime;
public:
	Content(const string& cont, const double& time) : content(cont), totalTime(time) {}
	string& getContent() { return content; }
	double& getTotalTime() { return totalTime; }
};

class Foot   // 日报尾
{
private:
	string name;
public:
	Foot(const string& name) :name(name) {}
	string& getName() { return name; }
};

class Builder   // 建造完整的日报
{
protected:
	string result;
public:
	virtual ~Builder() { }

	virtual void buildHead( Head * ) = 0;
	virtual void buildCont(vector<Content* >& vec) = 0;
	virtual void buildFoot(Foot*) = 0;
    string& getRes() { return result; }  // 返回左值引用
};

class Txt_builder : public Builder  // 建造 txt 型日报
{
public:
	void buildHead(Head* pHead) override
	{		
		result += pHead->getDepart() + " , " + pHead->getDate() + "\n";
	}
	void buildCont(vector<Content* >& vec) override
	{
		for (auto iter = vec.begin(); iter != vec.end(); iter++)
		{
			ostringstream oss;  // 此对象在每次 for 循环中都会被创建和释放,
			oss << (*iter)->getTotalTime();  // 所以输入到 oss 中的内容并不会积累。
			result += (*iter)->getContent() + " ,花费小时:" + oss.str() + '\n';
		}
	}
	void buildFoot(Foot* pFoot) override
	{
		result += "报告人:" + pFoot->getName() + '\n';
	}
};

class Assemble   // 本类可以组装各种日报: txt 、 xmL 、 Json
{
private: 
	Builder* builder;
public:
	Assemble(Builder* builder) : builder(builder) {}
	string& assemble(Head * head , vector<Content * >& vec , Foot * foot)
	{
		builder->buildHead(head);
		builder->buildCont(vec);
		builder->buildFoot(foot);

		return builder->getRes();
	}
};

int main()
{
	Head head("研发部" , "7.13");
	Content conA("分析文档"  , 3.5);
	Content conB("选定语言"  , 0.5);
	Foot foot("zhangwuji");

	vector<Content*> vecCont{&conA , &conB};

	Txt_builder txtBuilter;
	Assemble assemble(&txtBuilter);
	auto strResult = assemble.assemble(&head , vecCont , &foot);
	cout << strResult << endl;
	return 0;
}

这里给出测试结果:

在这里插入图片描述

(7) 策略模式 Strategy 。类似于框架模式:策略的框架不变,实现细节会变,但比框架模式更复杂一点。还是以上面的游戏角色为例,战士和法师都可以采用回血策略,回血策略又可以分为小药回小血,中药回中血,大药回大血的不同具体策略,甚至随着游戏扩展,还有新的策略,或修订原有的策略。

class Strategy;  // 前置声明,否则编译报错
class Player;

class Strategy
{
public:
	virtual ~Strategy() {}
	virtual void heal(Player*) = 0;
};


class Player  // 角色父类
{
protected:
	int life;
	int magic;
	int attack;
	Strategy* pStrategy ;
public:	
	virtual ~Player() {}    // 父类应有的虚析构函数
	Player(int life, int magic, int attack) : life(life), magic(magic), attack(attack), pStrategy(nullptr){}

	int getLife() { return life; }
	void setLife(int t) { life = t; }
	void setStrategy(Strategy* p) { pStrategy = p; }
	void heal() { pStrategy->heal(this); }
};

class Fighter : public Player   // 战士 
{
public:	
	Fighter(int life, int magic, int attack) : Player(life , magic , attack) {}
};

class Master : public Player  // 法师 
{
public:	Master (int life, int magic, int attack) : Player(life, magic, attack) {}
};

class Strategy_Small_Medi : public Strategy
{
public:
	void heal(Player* ptr) { ptr->setLife( ptr->getLife() + 200 ); }
};

class Strategy_Middle_Medi : public Strategy
{
public:
	void heal(Player* ptr) { ptr->setLife(ptr->getLife() + 300); }
};

int main()
{
	Fighter fight(100, 100, 100);
	Strategy_Small_Medi small;
	fight.setStrategy(&small);
	fight.heal();
	return 0;
}

(8) 观察者模式observer , 也叫 发布–订阅模式,Publish – Subscribe 。 订阅者就是观察者。因为发布者的信息要群发,所以就要注意提升遍历数据库时的效率,尽可能快速的把消息发布给所有的订阅者。重在良好组织所有对象的存储方式,加快对订阅者的遍历速度: 比如 map 容器的遍历就比 list 要快。

class Player;

class Manager   // 管理,充当数据库的角色,管理玩家角色的组信息
{
public:
	virtual ~Manager() {}
	virtual void joinFamily(Player *) = 0;
	virtual void leaveFamily(Player*) = 0;
	virtual void notify(Player* , const string & ) = 0;
};

class Player  // 角色父类
{
protected:
	int id;
	int familyID;
	string name;
public:	
	virtual ~Player() {}    // 父类应有的虚析构函数
	Player(int id, const string& name) : id(id), name(name) { familyID = -1; }

	void setFamily(int t) { familyID = t; }
	int getFamily() { return familyID; }
	void speak(const string& str, Manager* maga) { maga->notify(this, str); }
	void getNotice( Player * player , const string & str ) 
	{
		cout << name << "  的电脑显示  " << player->name << "  的信息: " << str << "\n\n";
	}
};

class Fighter : public Player   // 战士 
{
public: 	Fighter(int id, const string & name) : Player(id, name) {}
};

class Master : public Player   // 法师
{
public:  	Master(int id, const string& name) : Player(id, name) {}
};

class Manager_Fighter : public Manager // 采用默认的构造函数
{
private:
	map<int, list<Player*>> allFamily;  // 管理所有的有家族的角色,以家族的方式管理角色
public:
	virtual void joinFamily(Player* player) override
	{
		int familyID = player->getFamily();

		if (familyID != -1)  // 要插入家族树的角色必须具有有效的家族 id 
		{
			auto iter = allFamily.find(familyID);

			if (iter != allFamily.end())  // 该家族已在家族树中
				iter->second.push_back(player);
			else             // 出现了新的家族,要先把家族链表 list 创建并插入 map 中
			{
				list<Player*> list;
				allFamily.insert(make_pair(familyID , list));
				allFamily[familyID].push_back(player);  // 理解难度在于 STL 库容器中 map 的成员方法的使用
			}
		}
	}

	virtual void leaveFamily(Player * player) override
	{
		int familyID = player->getFamily();
		
		if (familyID != -1)
		{
			auto iter = allFamily.find(familyID);

			if (iter != allFamily.end())
				allFamily[familyID].remove(player);  // list 允许插入相同的节点。也会被全部删除。当然指针是不会相同的
		}
	}

	virtual void notify(Player* speaker, const string& str) override
	{
		int familyID = speaker->getFamily();
		
		if (familyID != -1)
		{
			auto iter = allFamily.find(familyID);

			if (iter != allFamily.end())
				for (auto iterList = iter->second.begin(); iterList != iter->second.end(); iterList++)
					(*iterList)->getNotice(speaker, str);
		}
	}
};


int main()
{
	Fighter zhang(10, "张三");
	Fighter zhao(11, "赵四");
	Fighter wang(12, "王五");
	Fighter ma(13, "马虎");
	Manager_Fighter managerFight;

	zhang.setFamily(100);
	zhao.setFamily(100);
	wang.setFamily(100);
	ma.setFamily(200);

	managerFight.joinFamily(&zhang);
	managerFight.joinFamily(&zhao);
	managerFight.joinFamily(&wang);
	managerFight.joinFamily(&ma);

	zhang.speak("Hello , i love you !!" , &managerFight);

	return 0;
}

以下给出测试结果:

在这里插入图片描述

(9) 装饰器模式 decorator 。比如显示器绘图,绘制控件 ,比如带框的列表框,就要先绘制列表框,再给其加框;绘制带滚动条的列表框,就要先绘制列表框,再绘制滚动条。有绘图的先后顺序。类继承不恰当时候,比如子类不需要父类的所有功能,就可以考虑类组合,一个类的数据成员是另一个类,以获得更灵活强大的功能。

class Control    // 控件基类
{
public:
	~Control() {}
	virtual void draw() = 0 ;
};

class Control_List : public Control   // 列表控件
{
public: 
	virtual void draw() override { cout << " 列表框绘制\n\n"; }

};

class Decorator : public Control  // 装饰器基类 : 其先要绘制被修饰的控件。但带修饰的控件依然可以再被修饰。
{
private: 
	Control* ptrControl;  // 被修饰的控件对象
public:
	Decorator(Control* p) : ptrControl(p) {}
	virtual void draw() override { ptrControl->draw() ; }  // 作为装饰类的基类的 draw()方法
};

class Decorator_border : public Decorator  // 装饰器:边框
{
public:
	Decorator_border(Control* p) : Decorator(p) {}
	virtual void draw() override
	{
		Decorator::draw();  // 此处可以出现递归调用,直到调用完所有父类的 draw()方法
		drawBorder();
	}
private:
	void drawBorder() { cout << " 边框绘制\n\n"; }
};

class Decorator_Horizontal_scroll_bar : public Decorator  // 装饰器:水平滚动条  
{
public:
	Decorator_Horizontal_scroll_bar(Control* p) : Decorator(p) {}
	virtual void draw() override
	{
		Decorator::draw();
		draw_Horizontal_scroll_bar();
	}
private:
	void draw_Horizontal_scroll_bar() { cout << " 水平滚动条绘制\n\n"; }
};

int main()
{
	Control_List ctrlList;
	Decorator_border decoBorder(&ctrlList);
	Decorator_Horizontal_scroll_bar decoBorderHSB(&decoBorder);

	decoBorderHSB.draw();

	return 0;
}

以下给出测试结果:

在这里插入图片描述

这里再给出装饰者模式的另一范例(上一例是电脑控件绘图),加佐料的饮料售卖:饮料最初售价 10 元,加了糖再加 2 元, 加了牛奶 再加 2 元。以下是代码,可见与电脑绘图出奇的像 :

class Drink                // 饮料的基类
{
public:
	virtual ~ Drink() {}
	virtual int getPrice() = 0;
};

class Drink_Fruit : public Drink   // 水果饮料,可以往里再加糖、牛奶
{
public:
	virtual int getPrice() override { return 10; }  // 啥也不加的水果饮料,价格是 10 元
};

class Decorator : public Drink
{
private:
	Drink* ptrDrink;
public:
	virtual ~Decorator() {}
	Decorator(Drink* p) : ptrDrink(p) {}
	virtual int getPrice() override { return ptrDrink->getPrice() ; }
};

class Decorator_Sugar : public Decorator  // 饮料里再加糖,加 2 元
{
public:
	Decorator_Sugar(Drink* p) : Decorator(p) {}
	virtual int getPrice() override { return Decorator:: getPrice() + 2 ; }
};

class Decorator_Milk : public Decorator   // 饮料里再加牛奶,加 3 元
{
public:
	Decorator_Milk(Drink* p) : Decorator(p) {}
	virtual int getPrice() override { return Decorator::getPrice() + 3; }
};

int main()
{
	Drink_Fruit drinkFruit;
	Decorator_Sugar decoSugar(&drinkFruit);
	Decorator_Milk  decoMilk(&decoSugar);

	cout << " 水果饮料,加糖,加牛奶后的价格: " << decoMilk.getPrice() << "\n\n";

	return 0;
}

测试结果如下:

在这里插入图片描述

谢谢

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

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

相关文章

golang 获取系统的主机 CPU 内存 磁盘等信息

golang 获取系统的主机 CPU 内存 磁盘等信息 要求 需要go1.18或更高版本 官方地址&#xff1a;https://github.com/shirou/gopsutil 使用 #下载包 go get github.com/shirou/gopsutil/v3/cpu go get github.com/shirou/gopsutil/v3/disk go get github.com/shirou/gopsuti…

C语言指针速成下篇

c语言的指针下篇终于迎来了收尾&#xff0c;那么废话不多说&#xff0c;我们直接进入正题 指针访问数组 # include <stdio.h> int main () { int arr[ 10 ] { 0 }; // 输⼊ int i 0 ; int sz sizeof (arr)/ sizeof (arr[ 0 ]); // 输⼊ int * p arr //这…

Linux命令 wc(word count)-l(lines)用于统计文件中的行数。

文章目录 1、wc -l2、实战3、wc --help 1、wc -l 在命令 wc -l 中&#xff0c;-l 的英文全称是 lines。这个选项用于指定 wc&#xff08;word count&#xff0c;单词计数&#xff09;命令来统计文件的行数。 例如&#xff0c;当你运行 wc -l load_user_100w_sort.sql 时&…

ElasticSearch安装、配置详细步骤

一、环境及版本介绍 操作系统&#xff1a; Windows 10 软件版本&#xff1a; elasticsearch-7.17.22、kibana-7.17.22、IK-7.17.22 开发环境选择软件版本应提前考虑正式系统环境&#xff0c;否则会产生软件与服务器环境不兼容的问题出现&#xff0c;ElasticSearch与环境支持…

龙迅#LT6911GXC支持HDMI2.1转MIPI/4PORT LVDS应用功能,分辨率高达8K30HZ/4K120HZ压缩格式。

1. 描述 该LT6911GXC是一款高性能HD-DVI2.1转MIPI或LVDS芯片&#xff0c;适用于VR/显示应用。 HDCP RX作为HDCP中继器的上游&#xff0c;可以与其他芯片的HDCP TX配合实现中继器功能。 对于 HD-DVI2.1 输入&#xff0c;LT6911GXC可以配置为 3/4 通道。 对于MIPI输出&#xff0c…

vue2使用wangEditor5搭建模拟文档的编辑器快速集成

如图 1、下载依赖 2、elm引入&#xff08;可省略&#xff09; main.js 或者 按需引入 3、cv <template><div style"background: #f1f3f4"><div style"width: 100%; height: 42px"><!-- 工具栏 --><Toolbarid"tool-conta…

SAP中通过财务科目确定分析功能来定位解决BILLING问题实例

接用户反馈&#xff0c;一笔销售订单做发货后做销售发票时&#xff0c;没有成功过账到财务&#xff0c;提示财户确定错误。 这个之前可以通过VF02中点击小绿旗来重新执行过财动作&#xff0c;看看有没有相应日志来定位问题。本次尝试用此方法&#xff0c;也没有找到相关线索。 …

英国牛津大学博士后职位—统计学

牛津大学&#xff08;University of Oxford&#xff09;&#xff0c;简称“牛津”&#xff08;Oxford&#xff09;&#xff0c;位于英国牛津&#xff0c;是一所公立研究型大学&#xff0c;采用传统学院制。是罗素大学集团成员&#xff0c;被誉为“金三角名校”、“G5超级精英大…

ICRA 2024 混变刚度的仿人软体手指实现多模式抓取

ICRA 2024 发表了"用于多模式抓取的具有混合可变刚度机制的仿生软指 "的研究工作。核心思想是利用记忆合金的形状记忆效应&#xff0c;构建结构简化、功能多样的柔性手指&#xff0c;从而实现更高效的多模式抓取。 与传统的刚性夹爪相比&#xff0c;柔性软体夹爪具有…

浅谈红队攻防之道-DLL注入上线cs

等我熬过这一段狼狈&#xff0c;一个人尝尽孤独的滋味&#xff0c;我会笑着与这个世界和解 0x1 DLL注入概念 DLL注入(DLL Injection)是一种计算机编程技术&#xff0c;它可以强行使另一个进程加载一个动态链接库(DLL)以在其地址空间内运行指定代码。常见用途是改变原先程序的…

首个实时 AI 视频生成技术发布;科大讯飞发布星火大模型 4.0 丨 RTE 开发者日报

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「…

SwiftUI 中的内容边距

文章目录 前言创建示例适配 iPad使用 contentMargins可运行 Demo总结前言 SwiftUI 引入了一组视图修饰符,使我们能够有效地管理视图中的安全区域。在许多情况下,安全区域是你希望放置内容的地方。今天,我们将了解 SwiftUI 引入的新内容边距概念以及它与安全区域的区别。 创…

老师如何发布学校分班情况?

随着新学期的临近&#xff0c;许多老师可能都会回想起过去那些忙碌的日子&#xff0c;他们不得不面对一堆学生名单&#xff0c;手动进行班级分配&#xff0c;然后逐一通知家长和学生&#xff0c;这种工作不仅繁琐而且容易出错&#xff0c;让人倍感压力。 然而&#xff0c;今天我…

地理空间数据格式GeoJSON扫盲,在CesiumJS中如何加载。

Hi&#xff0c;我是贝格前端工场&#xff0c;GIS已经越来越多的应用在可视化大屏中了&#xff0c;开发GIS类应用就少不了地理空间数据&#xff0c;本文介绍一下数据GeoJSON数据格式。 一、什么是GeoJSON数据格式&#xff0c;在GIS开发中有什么作用 GeoJSON是一种基于JSON&…

如何知道docker谁占用的显卡的显存?

文章目录 python环境安装nvidia-htop查看pid加一个追踪总结一下【找到容器创建时间】使用说明示例 再总结一下【用PID找到容器创建时间&#xff0c;从而找到谁创建的】使用说明示例 python环境安装nvidia-htop nvidia-htop是一个看详细的工具。 pip3 install nvidia-htop查看…

Windows部署MinIO,搭建本地对象存储服务

一、前言 二、MinIO介绍 三、Windows部署MinIO服务 1、准备工作 2、下载MinIO服务 3、启动MinIO服务 4、设置用户名密码 5、创建.bat文件启动服务 四、MinIO基本操作 1、存储桶管理 2、对象管理 3、数据查看 一、前言 基于外网的项目&#xff0c;可以使用阿里云等…

涨点超强!图像特征提取最新方法!性能效率快到飞起

在图像处理领域&#xff0c;有一个非常关键的步骤&#xff1a;图像特征提取。它能给我们提供一种高效、准确且灵活的方式来描述和分析图像内容。 通过降低图像数据的维度&#xff0c;去除冗余和噪声信息&#xff0c;图像特征提取不但简化了后续处理过程&#xff0c;还能提高算…

文本分析|小白教程

在信息爆炸的时代&#xff0c;文本数据无处不在&#xff0c;如何从这些海量的文字中提炼出有价值的信息呢&#xff1f;答案就是——文本分析。文本分析&#xff0c;简单来说&#xff0c;就是对文本数据进行深度的研究和分析。它能够从看似普通的文字中&#xff0c;提取出主题、…

老司机开发技巧,如何扩展三方包功能

前言 最近碰上有个业务&#xff0c;查询的sql如下&#xff1a; sql 复制代码 select * from table where (sku_id,batch_no) in ((#{skuId},#{batchNo}),...); 本来也没什么&#xff0c;很简单常见的一种sql。 问题是我们使用的是mybatis-plus&#xff0c;然后写的时候有没…