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

( 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.rhkb.cn/news/363427.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

51单片机定时炸弹-准确计时-两根线随机一根触发中断可“拆弹“(AT89C52)

一、设计介绍: 1、使用定时器按照精确时间读秒倒计时&#xff0c;倒计时在LCD1602中居中显示&#xff0c;格式为mm&#xff1a;ss&#xff0c;每秒变化一次 2、默认倒计时10分钟&#xff0c;时间到后显示“Time over”“&#xff08;&#xff08;&#xff08;Boom&#xff09…

抗击.michevol勒索病毒:保障数据安全的新策略

导言&#xff1a; 在今天高度互联的数字化环境中&#xff0c;数据安全面临着越来越复杂和普遍的威胁&#xff0c;勒索病毒如.michevol已成为了用户和企业普遍面临的风险。本文91数据恢复将探讨.michevol勒索病毒的特点、感染方式以及创新的防御策略&#xff0c;旨在帮助读者更…

【IJCAI2024】LeMeViT: Efficient Vision Transformer with Learnable Meta Tokens

【IJCAI2024】LeMeViT: Efficient Vision Transformer with Learnable Meta Tokens for Remote Sensing Image Interpretation 论文&#xff1a;https://arxiv.org/abs/2405.09789 代码&#xff1a;https://github.com/ViTAE-Transformer/LeMeViT 由于相邻像素和图像块之间的高…

Solana最新上线的Blink功能引爆加密圈:杀手级应用还是花拳绣腿?

近日&#xff0c;Solana推出了其最新功能——Blink&#xff0c;这一功能迅速引爆了加密圈的讨论。Blink功能能够将链上的操作&#xff0c;如交易、投票、支付、铸币等转化为一个可分享的链接或二维码&#xff0c;允许用户在不跳转其他页面的情况下直接在推特上完成上述链上操作…

【代码工厂】简单地图生成

要求 &#xff08;图片来自codingame&#xff09; 代码 # 定义一个函数&#xff0c;用于生成模式 def generate_pattern(n, a, border_char): # 初始化一个空列表&#xff0c;用于存储生成地图pattern []# 最上面那一行的处理line n * border_charpattern.append(line)# 遍…

建投数据人力资源管理系统APP完成迭代升级

近日&#xff0c;建投数据人力资源管理系统APP完成迭代升级。 此次升级思路&#xff0c;遵循提升移动应用的功能和用户体验&#xff1b;直观的界面、快速的响应速度和安全的数据存储&#xff1b;个性化的功能&#xff0c;以满足不同员工的需求和使用偏好。 人力资源管理系统A…

行业推荐!IG5216量产工具下载,IG5216开卡软件分享

国内固态硬盘常用&#xff0c;且有量产工具流传出来的主控厂商包括慧荣、群联、点序、英韧、得一微、瑞昱、联芸、迈威、国科、华澜微等等。 每个主控需要用各自对应的量产工具&#xff0c;不同的量产工具支持的闪存颗粒也有差异&#xff0c;因此要根据固态硬盘实际的主控型号…

SpringBoot集成道历(实现道历日期查询)

官网地址&#xff1a;官网地址https://6tail.cn/calendar/api.html 1、导入依赖 <dependency><groupId>cn.6tail</groupId><artifactId>lunar</artifactId><version>1.3.9</version></dependency><dependency><group…

【UE5.3】笔记6-第一个简单小游戏

打砖块小游戏&#xff1a; 1、制造一面砖块组成的墙 在关卡中放置一个cube&#xff0c;放这地面上&#xff0c;将其转换成蓝图类,改名BP_Cube&#xff0c;更换砖块的贴图&#xff0c;按住alt键进行拷贝&#xff0c;堆出一面墙&#xff0c;复制出来的会很多&#xff0c;全选移动…

Servlet工作原理

Servlet 工作原理 编写Servlet 创建servlet 创建一个MyServlet继承HttpServlet&#xff0c;重写doGet和doPost方法&#xff0c;也就是看请求的方式是get还是post&#xff0c;然后用不同的处理方式来处理请求&#xff0c; 2. 配置Servlet //添加参数 <servlet><se…

项目开发 TCP-Socket连接功能实现(Android端)

前段时间在公司做项目的时候遇到了一个功能需要使用TCP-Socket连接硬件设备进行通信&#xff0c;查了很多资料也只是关于HTTP-Socket相关的&#xff0c;没法满足项目的要求&#xff0c;后来查到一个相关的插件&#xff0c;现在有时间和大家分享一下。 项目简单介绍&#xff1a…

来聊聊nacos

先关注下下方公众号呗&#xff1a; 第1部分&#xff1a;引言 微服务的挑战 尽管微服务架构带来了许多好处&#xff0c;如敏捷性、可扩展性和容错性&#xff0c;但它也带来了一些挑战&#xff0c;特别是在服务发现、配置管理、服务间通信和运维管理方面。这些挑战需要有效的解…

经验分享之会员 SaaS 系统

前言 2018年&#xff0c;这是不平凡的一年&#xff1b;互联网行业的中台战略、会员经济等模式如火如荼&#xff0c;同时也逐渐地走入我们公司每个人的视野。在南海集团的战略规划背景下&#xff0c;当时我所在的公司作为集团的研发中心&#xff0c;承担了对会员 SaaS 系统的建…

一个人 三个月 干了二十万

相信很多人是被这个标题吸引进来的&#xff0c;但我并不是标题党&#xff0c;我也很讨厌标题党&#xff0c;这篇文章也不在乎流量&#xff0c;更多的是想记录下。 出来创业三个多月了&#xff0c;给大家汇报一下这段时间的业绩吧。一个人&#xff0c;三个多月&#xff0c;干了…

北邮《计算机网络》传输层笔记

内容一览 缩写复习单词复习传输层前言传输协议的要点拥塞控制UDPTCP VS UDPTCP 缩写复习 AIMD XCP ECN WFQ max-min-fair ARQ PAWS TSAP NSAP TCP UDP RTT SCTP SACK NAK RST MSS 单词复习 inverse multiplexing(SCTP) convergence crashed machine protocol scenarios asym…

针对VMWare无法使用鼠标功能键问题

在使用 VMWare 虚拟机的Ubuntu系统时发现无法使用许多鼠标带有额外的功能键&#xff0c;比如常用的前进后退&#xff0c;但是双系统中的Ubuntu没有问题&#xff0c;后来一搜发现是&#xff0c;虚拟系统中不支持这些功能键。因此我们对这个问题进行了解决。 解决方案 1.找到自…

bable 【实用教程】

简介 bable 用于将 ES6 的语法编译为 ES5 只关心语法&#xff0c;不关心 API 是否正确。不处理模块化&#xff08;webpack 会处理&#xff09; 搭建开发环境 安装相关的包 npm i babel/cli babel/core babel/preset-env新建文件 .babelrc&#xff0c;内容为 { "presets…

已解决javax.transaction.InvalidTransactionException:事务无效的正确解决方法,亲测有效!!!

已解决javax.transaction.InvalidTransactionException&#xff1a;事务无效的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 报错原因 解决思路 解决方法 1. 确保事务的正确启动和结束 Spring中的事务管理 2. 避免嵌套事务问题…

Linux显示服务器Wayland切换到X11

1、临时切换 &#xff1a; 注销当前用户账户&#xff0c;返回到登录屏幕。 在登录屏幕上&#xff0c;选择您要登录的用户账户。 在输入密码之前&#xff0c;在登录屏幕的右下角可能有一个齿轮图标&#xff0c;点击它以展开更多选项。 在选项中选择“Ubuntu on Xorg”或“Ubu…

【uml期末复习】统一建模语言大纲

前言&#xff1a; 关于uml的期末复习的常考知识点&#xff0c;可能对你们有帮助&#x1f609; 目录 第一部分 概念与基础 第一章 面向对象技术 第二章 统一软件过程 第三章 UML概述 第四章 用例图 第五章 类图 第六章 对象图 第七章 顺序图 第八章 协作图 第九章 状态…