一文让你搞懂设计模式

羽林君 2022-11-03 19:45


最近在研究设计模式,发现尽管设计模式是针对面向对象语言提出的,但貌似市面上大多数都是基于java给出的例子,C++的例子极少,自己看完李建忠老师的GOF设计模式视频后查阅各种资料后,把主要的设计模式用C++代码实现了一套,心得总结如下,希望对大家有所帮助。


设计模式简介

设计模式是主要针对面向对象语言提出的一种设计思想,主要是提高代码可复用性,抵御变化,尽量将变化所带来的影响降到最低。

面向对象特点

  • 封装:隐藏内部实现

  • 继承:复用现有的代码

  • 多态:改写对象的行为

面向对象设计原则

  1. 依赖倒置原则:针对接口编程,依赖于抽象而不依赖于具体,抽象(稳定)不应依赖于实现细节(变化),实现细节应该依赖于抽象,因为稳定态如果依赖于变化态则会变成不稳定态。

  2. 开放封闭原则:对扩展开放,对修改关闭,业务需求是不断变化的,当程序需要扩展的时候,不要去修改原来的代码,而要灵活使用抽象和继承,增加程序的扩展性,使易于维护和升级,类、模块、函数等都是可以扩展的,但是不可修改。

  3. 单一职责原则:一个类只做一件事,一个类应该仅有一个引起它变化的原因,并且变化的方向隐含着类的责任。

  4. 里氏替换原则:子类必须能够替换父类,任何引用基类的地方必须能透明的使用其子类的对象,开放关闭原则的具体实现手段之一。

  5. 接口隔离原则:接口最小化且完备,尽量少public来减少对外交互,只把外部需要的方法暴露出来。

  6. 最少知道原则:一个实体应该尽可能少的与其他实体发生相互作用。

  7. 将变化的点进行封装,做好分界,保持一侧变化,一侧稳定,调用侧永远稳定,被调用测内部可以变化。

  8. 优先使用组合而非继承,继承为白箱操作,而组合为黑箱,继承某种程度上破坏了封装性,而且父类与子类之间耦合度比较高。

  9. 针对接口编程,而非针对实现编程,强调接口标准化

总结: 没有一步到位的设计模式,刚开始编程时不要把太多精力放到设计模式上,需求总是变化的,刚开始着重于实现,一般敏捷开发后为了应对变化重构再决定采取合适的设计模式。

模板方法

父类定义算法的骨架,而将一些步骤延迟到子类去实现,使得子类可以复用骨架,并附加特性,以开发框架举例,框架开发人员把框架调用流程定好,而将某些具体的步骤作为虚函数留给子类去重写,话不多说,上代码。

#ifndef __GAME__#define __GAME__
#include
class Game { public: Game() {}
virtual ~Game() {}
void Run() { InitGame(); StartGame(); StopGame(); }
protected: virtual void StartGame() { std::cout << "step 2: start game" << std::endl; }
private: void InitGame() { std::cout << "step 1: init game" << std::endl; } void StopGame() { std::cout << "step 3: stop game" << std::endl; }};
#endif
#include "game.h"
class BasketBall : public Game { void StartGame() override { std::cout << "start basketball game" << std::endl; }};
#include "game.h"
class SocketBall : public Game { void StartGame() override { std::cout << "start socketball game" << std::endl; }};
#include "basketball.h"#include "socketball.h"
int main() { Game *game = new BasketBall(); game->Run(); delete game; Game *game2 = new SocketBall(); game2->Run(); delete game2; return 0;}g++ test.cc -std=c++11 && ./a.out输出:step 1: init gamestart basketball gamestep 3: stop gamestep 1: init gamestart socketball gamestep 3: stop game

代码很简单,体现的是思想,游戏包含三个步骤,初始化游戏,开始游戏,停止游戏,初始化游戏和停止游戏步骤比较统一,由父类Game定义好,而开始游戏是第二个步骤,可以有打篮球和踢足球,将来也可以有羽毛球,乒乓球等等,每增加一项运动,都可以从Game父类中继承后重写开始游戏这个函数,达到不同的功能,符合模板方法的特性,即如何在确定稳定结构前提下,应对子步骤需求的变化

策略模式

定义一系列的算法,将它们一个个封装,使得他们可以相互替换,一般为了解决多个if-else带来的复杂性,在多种算法相似的情况下,通过策略模式可减少if-else带来的复杂性和难以维护性,一般在项目中发现多个if-else并且预感将来还会在此增加if-else分支,那基本上就需要使用策略模式。
先举一个不使用策略模式的例子,拿计算来说,下面代码定义了加法操作和减法操作,以后如果需要增加乘法除法等计算,那就需要在枚举里添加新类型,并且增加if-else分支,这违反了开放关闭原则。

enum class CalOperation {    add,    sub};
int NoStragegy(CalOperation ope) { if (ope == CalOperation::add) { std::cout << "this is add operation" << std::endl; } else if (ope == CalOperation::sub) { std::cout << "this is sub operation" << std::endl; } // 如何将来需要增加乘法或者除法或者其它运算,还需要增加if-else return 0;}
下例为使用策略模式,定义一个基类Calculation,包含虚函数operation()。
#ifndef __CALCULATION__#define __CALCULATION__
#include
class Calculation { public: Calculation() {}
virtual ~Calculation() {}
virtual void operation() { std::cout << "base operation" << std::endl; }};
#endif
每增加一种运算,就增加一个继承基类的子类,重写operation()函数。
#ifndef __ADD__#define __ADD__
#include "calculation.h"
class Add : public Calculation { void operation() override { std::cout << "this is add operation" << std::endl; }};
#endif
#ifndef __SUB__#define __SUB__
#include "calculation.h"
class Sub : public Calculation { void operation() override { std::cout << "this is sub operation" << std::endl; }};
#endif
int Stragegy() {    Calculation *cal = new Add();    cal->operation();    delete cal;
Calculation *cal2 = new Sub(); // 这里将来都可以用工厂模式改掉,不会违反开放封闭原则 cal2->operation(); delete cal2;
return 0;}

是不是方便了很多,将来如果有乘法除法和其它运算规则,只需要再加一个继承基类的子类即可。

观察者模式

定义对象间的一对多关系,当一个对象状态发生改变的时候,其它依赖于它的对象都会得到广播通知并进行自定义动作,通过面向对象技术的多态技术,可以降低这种依赖关系,降低耦合度,上代码。

#ifndef __OBSERVER__#define __OBSERVER__
#include
class ObserverBase { public: ObserverBase() {} virtual ~ObserverBase() {}
virtual void Update() {}};
#endif
#ifndef __OBSERVER_FIRST_CHILD__#define __OBSERVER_FIRST_CHILD__
#include "observer.h"
class ObserverFirstChild : public ObserverBase { void Update() override { std::cout << "first child receive notify" << std::endl; }};
#endif
#ifndef __OBSERVER_SECOND_CHILD__#define __OBSERVER_SECOND_CHILD__
#include "observer.h"
class ObserverSecondChild : public ObserverBase { void Update() override { std::cout << "second child receive notify" << std::endl; }};
#endif
#include #include "observer_first_child.h"#include "observer_second_child.h"
class NotifyBase { public: void Add(ObserverBase *ob) { observers.emplace_back(ob); };
void Remove(ObserverBase *ob) { observers.remove(ob); }
void Notify() { for (auto observer : observers) { observer->Update(); } }
private: std::list observers;};
int main() { ObserverBase *base1 = new ObserverFirstChild(); ObserverBase *base2 = new ObserverSecondChild();
NotifyBase notify; notify.Add(base2); notify.Add(base1); notify.Notify(); notify.Remove(base1); notify.Notify();
delete base2; delete base1; return 0;}

通过观察者模式可以灵活的控制依赖的对象,动态的增加和删除需要得到通知的对象。


装饰器模式

动态的给一个对象添加一些额外的职责,扩展一个类的功能,就增加功能来说,使用装饰器模式比单纯的继承子类更加灵活,不一定非要疯狂使用继承方式。
举个例子,有游戏这个大类,扩展这个类的功能,有打篮球,踢足球,玩lol,玩卡丁车,可以分别定义继承游戏基类的四个子类,但是如果想组合这几个功能呢,一个对象既会打篮球又会玩卡丁车,既会打篮球又会玩lol,再定义几个类继承游戏基类显然不是好的做法,装饰器模式可以很好的解决这个问题,上代码:
首先定义一个Game基类。

#ifndef __GAME__#define __GAME__
#include
class Game { public: Game() {}
virtual ~Game() {}
virtual void Skill() { std::cout << "game skill" << std::endl; }};
#endif

定义卡丁车子类和lol子类

#ifndef __CAR_GAME__#define __CAR_GAME__
#include "game.h"
class CarGame : public Game { public: void Skill() override { std::cout << "car game" << std::endl; }};
#endif
#ifndef __LOL_GAME__#define __LOL_GAME__
#include "game.h"
class LolGame : public Game { public: void Skill() override { std::cout << "lol game" << std::endl; }};
#endif

篮球和足球子类也可以像上面一样继承定义,但是如果都像上面一样继承的话不能实现动态扩展功能的需求,所以先定义一个装饰类,之后定义继承这个装饰类的篮球和足球子类。

#ifndef __DECORATOR_GAME__#define __DECORATOR_GAME__
#include "game.h"
class DecoratorGame : public Game { protected: Game *game_;
public: DecoratorGame(Game *game) { game_ = game; }
void Skill() override { game_->Skill(); }
virtual ~DecoratorGame() {}};
#endif
#ifndef __BASKETBALL__#define __BASKETBALL__
#include "decorator_game.h"
class BasketBallGame : public DecoratorGame { public: BasketBallGame(Game *game) : DecoratorGame(game) {}
void Skill() override { std::cout << "basketball game" << std::endl; DecoratorGame::Skill(); }};
#endif
#ifndef __SOCKETBALL__#define __SOCKETBALL__
#include "decorator_game.h"
class SocketBallGame : public DecoratorGame { public: SocketBallGame(Game *game) : DecoratorGame(game) {}
void Skill() override { std::cout << "socket game" << std::endl; DecoratorGame::Skill(); }};
#endif

最后看使用

#include "lol_game.h"#include "decorator_game.h"#include "basketball.h"#include "car_game.h"#include "socketball.h"
int main() { Game *lol = new LolGame(); Game *car = new CarGame(); // 一个人既会打篮球又会打LOL可以如下使用 Game* basketball_lol = new BasketBallGame(lol); basketball_lol->Skill(); std::cout << std::endl; // 一个人既会打篮球又会玩卡丁车可以如下使用 Game* basketball_car = new BasketBallGame(car); basketball_car->Skill(); std::cout << std::endl; // 一个人既会打篮球又会玩卡丁车又会踢足球可以如下使用 Game* g = new SocketBallGame(basketball_lol); g->Skill();
delete lol; delete basketball_car; delete basketball_lol; delete g;
return 0;}// 输出basketball gamelol game
basketball gamecar game
socket gamebasketball gamelol game

通过上例可以看出,使用装饰器模式可以动态的扩展类的职责,动态的组合类的各个功能,当看代码时发现一个类既继承了父类同时又持有父类的对象指针,那这基本上就是装饰器模式。

桥接模式

确定好抽象部分和实现部分,将抽象部分和实现部分分离,使得他们可以独立的变化,实现系统和抽象系统可能各自都会有各自的变化,使用桥接模式可以更加灵活的扩展,方法:用组合代替继承。
举例:一个图形基类,可以有圆形矩形多边形等等,每个都需要从图形基类中继承出一个子类,但是每个子类还需要画图,画颜色,圆形可能需要黄色黑色白色蓝色等等,矩形也可能需要黄色黑色白色蓝色等等,这种如果单纯的使用继承就不太灵活,可以使用桥接模式,把图形种类当作抽象部分,画颜色当作实现部分,使用组合的方式将抽象部分和实现部分分离,上代码:
首先将实现部分(画图)提取出来,同时多种画图方式从这个基类继承后重写,之后会作为Shape图形类的一个成员变量。

#ifndef __SHAPE_DRAW_IMPL__#define __SHAPE_DRAW_IMPL__
#include
class ShapeDrawImpl { public: virtual void Draw() = 0;
virtual ~ShapeDrawImpl() {}};
#endif
#ifndef __SHAPE_DRAW_IMPL_RED__#define __SHAPE_DRAW_IMPL_RED__
#include "shape_draw_impl.h"
class ShapeDrawImplRed : public ShapeDrawImpl{ public: virtual void Draw() override { std::cout << "draw red" << std::endl; }};
#endif
#ifndef __SHAPE_DRAW_IMPL_BLACK__#define __SHAPE_DRAW_IMPL_BLACK__
#include "shape_draw_impl.h"
class ShapeDrawImplBlack : public ShapeDrawImpl{ public: virtual void Draw() override { std::cout << "draw black" << std::endl; }};
#endif

上面定义了两种颜色的实现方式,红色和黑色,下面定义图形的类,定义一个图形基类,持有画图实现的句柄,之后定义多个继承图形基类的子类,圆形子类和矩形子类。

#ifndef __SHAPE__#define __SHAPE__
#include "shape_draw_impl.h"
class Shape { protected: ShapeDrawImpl *impl;
public: virtual void Update() {} Shape(ShapeDrawImpl *impl_) : impl(impl_) {}};
#endif
#ifndef __SHAPE_CIRCLE__#define __SHAPE_CIRCLE__
#include "shape.h"
class ShapeCircle : public Shape {public: void Update() override { std::cout << "circle shape update" << std::endl; impl->Draw(); }
ShapeCircle(ShapeDrawImpl *imp) : Shape(imp) {}};
#endif
#ifndef __SHAPE_RECTANGLE__#define __SHAPE_RECTANGLE__
#include "shape.h"
class ShapeRectangle : public Shape { public: void Update() override { std::cout << "rectangle shape update" << std::endl; impl->Draw(); }
ShapeRectangle(ShapeDrawImpl *imp) : Shape(imp) {}};
#endif
#include "shape_circle.h"#include "shape_rectangle.h"#include "shape_draw_impl_black.h"#include "shape_draw_impl_red.h"
int main() { ShapeDrawImpl *impl = new ShapeDrawImplBlack(); Shape* circle = new ShapeCircle(impl); circle->Update();
delete impl; delete circle;
return 0;}输出:circle shape updatedraw black

通过桥接模式可以更好的应对变化,应对抽象和实现的多种组合变化。

工厂模式

工厂模式属于创建型模式,主要用于创建对象时不向外部暴露创建逻辑,通过一个共同的接口指向新创建的对象,通过面向对象的多态,将创建对象的工作延迟到子类执行,由子类决定实例化哪个对象。用于隔离对象使用者与其具体类型之间的耦合关系,当具体类型经常变化时,可以考虑使用工厂模式。有一个类型的抽象基类,同时又有很多继承该抽象基类的具体类型,我们做的就是使其依赖于抽象而不依赖于具体,实现方式是创建一个工厂基类,在为每个具体类型定义一个可以创建其相应对象的工厂,每一个具体类对应一个具体工厂,工厂类继承自工厂基类,通过工厂基类的多态性就可以决定创建什么类型的对象。上代码:

#ifndef __GAME__#define __GAME__
#include
class Game { public: Game() {} virtual ~Game() {}
virtual void Play() { std::cout << "play game" << std::endl; }};
#endif
#ifndef __BASKETBALL__#define __BASKETBALL__
#include "game.h"
class BasketBall : public Game { void Play() override { std::cout << "play basketball" << std::endl; }};
#endif
#ifndef __SOCKETBALL__#define __SOCKETBALL__
#include "game.h"
class SocketBall : public Game { void Play() override { std::cout << "play socketball" << std::endl; }};
#endif

可以通过游戏工厂选择创建不同游戏类型的对象

#ifndef __GAME_FACTORY__#define __GAME_FACTORY__
#include "game.h"
class GameFactory { public: GameFactory() {} virtual ~GameFactory() {}
virtual Game* CreateGame() = 0;};
#endif
#ifndef __BASKETBALL_FACTORY__#define __BASKETBALL_FACTORY__
#include "game_factory.h"#include "basketball.h"
class BasketBallFactory : public GameFactory { public: Game* CreateGame() override{ return new BasketBall(); };};
#endif
#ifndef __SOCKETBALL_FACTORY__#define __SOCKETBALL_FACTORY__
#include "game_factory.h"#include "socketball.h"
class SocketBallFactory : public GameFactory { public: Game* CreateGame() override{ return new SocketBall(); };};
#endif
#include "basketball_factory.h"#include "socketball_factory.h"
int main() { GameFactory* factory = new BasketBallFactory(); Game* game = factory->CreateGame(); game->Play();
delete factory; delete game;
factory = new SocketBallFactory(); game = factory->CreateGame(); game->Play();
delete factory; delete game;
return 0;}输出:play basketballplay socketball

当有新类型增加时,需要添加一个具体类和一个相应的创建工厂,尽管减少了耦合度,但是其实还是比较麻烦的。

抽象工厂方法模式

和工厂方法模式类似,不做过多介绍,说一下定义,抽象工厂方法模式主要提供一个接口,让该接口负责创建多系列“相关或相互的对象”,无需指定具体的类,系列对象指的是某一特定系列下的对象间有相互依赖或相互作用的关系,不同系列的对象之间不能相互依赖,如果没有多系列对象创建的需求变化,没必要使用抽象工厂方法模式,使用简单工厂方法模式就可以,拿上一个举例继续说,游戏类型是一个系列,我们有了一个游戏类型的工厂,以后可能会再加入娱乐圈类型的一个系列,那就在做一个娱乐圈类型的系列工厂,以后可能还有文艺类型的系列,那就再加一个文艺类型的系列工厂,这就有了三个系列的工厂,所以就可以在这三类工厂的基础上再抽象出一个抽象的超级工厂,根据不同需求选择实例化哪一个系列的具体工厂,再创建具体工厂下的具体类型的对象。

原型模式

用于创建重复的对象,定义一个clone接口,通过调用clone接口创建出与原来类型相同的对象,上代码:

#ifndef __GAME__#define __GAME__
#include
class Game { public: virtual Game* clone() = 0;
virtual void Play() = 0;};
#endif
#ifndef __BASKETBALL__#define __BASKETBALL__
#include "game.h"
class BasketBall : public Game { virtual Game* clone() override { return new BasketBall(); }
virtual void Play() override { std::cout << "basketball" << std::endl; }};
#endif
#include "basketball.h"
int main() { Game *game = new BasketBall(); game->Play(); Game* new_game = game->clone(); new_game->Play();
delete game; delete new_game;
return 0;}

单纯看game不知道它是什么类型,它可能是篮球游戏也可能是足球游戏等,如果想创建一个与它相同类型的对象就可以使用原型模式其实就是实现一个clone接口,如果一个对象的拷贝构造函数比较复杂而自己不想使用拷贝构造的方式创建对象也可以使用原型模式,使用方式见上例。

建造者模式

用于构建一个复杂的大的对象,一个复杂的对象通常需要一步步才可以构建完成,建造者模式强调的是一步步创建对象,并通过相同的构建过程可以获得不同的结果对象,一般来说建造者对象不是直接返回的,与抽象工厂方法区别是抽象工厂方法用于创建多个系列的对象,而建造者模式强调一步步构建对象,并且构建步骤固定,举例:想要构建一个老师类的对象,老师有第一技能和第二技能,第一技能是数学就代表是数学老师,第一技能是英语就代表是语文老师,构造一个老师对象需要先设定老师的第一技能再设定老师的第二技能,强调一步步设定技能,将这一步步的构建过程可以抽象出建造者模式。
首先定义老师类

#ifndef __TEACHER__#define __TEACHER__
#include #include
class Teacher { private: std::string first_skill_; std::string second_skill_;
public: void SetFirstSkill(const std::string &skill) { this->first_skill_ = skill; std::cout << "first_skill:" << skill.c_str() << std::endl; }
void SetSecondSkill(const std::string &skill) { this->second_skill_ = skill; std::cout << "second_skill:" << skill.c_str() << std::endl;    }
};
#endif

定义一个老师的抽象构建器,再根据具体老师继承出具体的老师构建器

#ifndef __TEACHER_ABSTRACT_BUILDER__#define __TEACHER_ABSTRACT_BUILDER__
#include "teacher.h"
class TeacherAbstractBuilder { public: TeacherAbstractBuilder() {} virtual ~TeacherAbstractBuilder() {}
virtual void BuildFirstSkill() = 0; virtual void BuildSecondSkill() = 0;
virtual Teacher* GetTeacher() = 0;};
#endif
#ifndef __MATH_TEACHER_BUILDER__#define __MATH_TEACHER_BUILDER__
#include "teacher_abstract_builder.h"
class MathTeacherBuilder : public TeacherAbstractBuilder { public: MathTeacherBuilder() { this->teacher_ = new Teacher(); }
~MathTeacherBuilder() { delete this->teacher_; }
Teacher* GetTeacher() { return this->teacher_; }
void BuildFirstSkill() { this->teacher_->SetFirstSkill("math"); }
void BuildSecondSkill() { this->teacher_->SetSecondSkill("english"); }
private: Teacher* teacher_;};
#endif
#ifndef __ENGLISH_TEACHER_BUILDER__#define __ENGLISH_TEACHER_BUILDER__
#include "teacher_abstract_builder.h"
class EnglishTeacherBuilder : public TeacherAbstractBuilder { public: EnglishTeacherBuilder() { this->teacher_ = new Teacher(); }
~EnglishTeacherBuilder() { delete this->teacher_; }
Teacher* GetTeacher() { return this->teacher_; }
void BuildFirstSkill() { this->teacher_->SetFirstSkill("english"); }
void BuildSecondSkill() { this->teacher_->SetSecondSkill("math"); }
private: Teacher* teacher_;};
#endif

定义一个稳定的Director类,由它根据具体的老师构建器决定构建哪一个老师

#ifndef __DIRECTOR__#define __DIRECTOR__
#include "teacher_abstract_builder.h"
class Director { public: Director(TeacherAbstractBuilder* builder) { this->builder_ = builder; }
~Director() {}
void Create() { this->builder_->BuildFirstSkill(); this->builder_->BuildSecondSkill(); }
private: TeacherAbstractBuilder* builder_;};
#endif

使用方法如下:

#include "director.h"#include "english_teacher_builder.h"#include "math_teacher_builder.h"
int main() { TeacherAbstractBuilder *builder = new MathTeacherBuilder(); Director *director = new Director(builder);    director->Create(); delete builder; delete director;
builder = new EnglishTeacherBuilder(); director = new Director(builder);    director->Create(); delete builder; delete director;
return 0;}

通过Director利用不同的具体构建器都采用相同的步骤一步步构建出不同的具体的老师类对象。

单例模式

不多说了,网上太多了,列一段单例的代码:

static SingleTon& GetInstance() {        static SingleTon t;        return t; }
享元模式

通过共享技术有效支持大量细粒度的对象,主要解决面向对象代价问题,通过共享有效降低创建的对象个数,类似于对象池。
举例:篮球分多种颜色,我想要蓝色的篮球想要红色的篮球这就需要创建两个对象,当我再想要一个蓝色的篮球如果能够继续使用之前的那个对象就更好,通过享元模式可以做到。

#ifndef __BASKETBALL__#define __BASKETBALL__
#include #include
class BasketBall { public: BasketBall(const std::string& color) : color_(color) {} ~BasketBall() {} void Ball_Color(){ std::cout << "ball color:" <std::endl; } private: std::string color_;};
#endif
#ifndef __BASKETBALL_FACTORY__#define __BASKETBALL_FACTORY__
#include #include "basketball.h"
class BasketballFactory { private: std::unordered_map<std::string, BasketBall*> map_;
public: BasketBall* GetBasketBallWithColor(const std::string& color) { if (map_.find(color) == map_.end()) { BasketBall* p = new BasketBall(color); map_[color] = p; return p; } else { return map_[color]; } }};
#endif
#include "BasketballFactory.h"
int main() { BasketballFactory factory; BasketBall* white_ball1 = factory.GetBasketBallWithColor("white"); BasketBall* black_ball1 = factory.GetBasketBallWithColor("black"); BasketBall* white_ball2 = factory.GetBasketBallWithColor("white"); BasketBall* black_ball2 = factory.GetBasketBallWithColor("black"); white_ball1->Ball_Color(); white_ball2->Ball_Color(); black_ball1->Ball_Color(); black_ball2->Ball_Color(); // remember to delete return 0;}
外观模式(Facade Pattern)

感觉外观模式没啥特别的,感觉就是做好功能之间边界的划分,做好封装,弄清楚哪部分是稳定的,哪部分是变化的,对外稳定松耦合,对内迭代变化高内聚,子系统的内部外部要做好解耦,为子系统的一组接口提供一个稳定一致的(界面),子系统中的任何变化都不会影响这个(界面)的变化。

代理模式(Proxy Pattern)

为其它对象提供一种代理以控制这个对象的访问,一般对于比较大的对象访问比较困难或者或带来很多麻烦,例如裸指针的使用,所以一般使用智能指针来控制裸指针,使用智能指针也是一种代理模式,举例:A喜欢B想送给B礼物,但是却不认识B,而C却认识B,所以可以把C作为A的代理去送给B礼物,代码:

#ifndef __GIRL__#define __GIRL__
#include #include
class Girl { private: std::string name_;
public: Girl(const std::string &name) : name_(name) {}
std::string GetName() const noexcept { return name_; }};
#endif
#ifndef __GIFT__#define __GIFT__
#include "girl.h"
class Gift { public: virtual void GiveGift() = 0;};
#endif
#ifndef __GIFT_PROXY__#define __GIFT_PROXY__
#include "gift.h"#include "girl.h"
class GiftProxy : public Gift { public: GiftProxy(Girl girl) : girl_(girl) {}
void GiveGift() override { std::cout << "send " << girl_.GetName() << " gift" << std::endl; }
private: Girl girl_;};
#endif
#include "gift_proxy.h"
int main() { Girl lili("lili"); GiftProxy proxy(lili); proxy.GiveGift(); return 0;}

如上述代码,通过代理模式就可以给代码中的丽丽送礼物。

适配器模式

太常见了,每个人都会用,两个不兼容的接口之间的桥梁,就像耳机转换头,充电器转换头等等都是适配器模式,将一个类的接口转换为客户希望的另一种接口的形式,使得原本由于接口不兼容而不能一起工作的类可以一起工作。

中介模式

使用一个中介对象来封装一系列的对象交互,当多个对象间互相引用且操作比较复杂时可以考虑使用中介模式。如下图所示:左侧五个对象互相依赖,通过中介这个桥梁就可以减少这个依赖。

状态模式

当一个对象的行为依赖于它的状态并且其有很多种状态而且将来还会有更多状态时,如果使用简单的if-else来增加新状态就违反了面向对象的开闭原则,这时可以考虑使用状态模式,将具体的状态做出一个抽象类,也类似于工厂模式,将具体的状态分散于各个子类中,避免了更多的if-else分支,上代码:

#include using namespace std;
class Context;
class State { public: virtual void Handle(Context *context) = 0;};
class Context { public: Context(State *state) : state_(state) {}
void Request() { if (state_) { state_->Handle(this); } }
void ChangeState(State *pState) { state_ = pState; }
private: State *state_;};
class ConcreteStateA : public State { public: void Handle(Context *context) override { cout << "I am state a" << endl; }};
class ConcreteStateB : public State { public: void Handle(Context *context) override { cout << "I am state b" << endl; }};
int main() { State *state_a = new ConcreteStateA(); State *state_b = new ConcreteStateB(); Context *context = new Context(state_a);
context->Request(); context->ChangeState(state_b); context->Request();
delete context; delete state_b; delete state_a;
return 0;}
总结
个人认为这篇文章写的不是很成功,一个是自己水平有限,对设计模式的理解还不够,还没有摸透设计模式的精髓,二是自己写作能力更有限,文章中没有生动形象的图片描述,这是程序喵后续需要改进的地方,最近也会多研究研究设计模式,争取三个月内产出一篇高质量的设计模式文章,大家多多关注,公众号申请的晚,没有内置留言功能,大家有问题可以点击下方链接的小程序去留言。




RAII妙用之ScopeExit

深入浅出虚拟内存

深入浅出虚拟内存(二)绘制虚拟内存排布图

深入浅出虚拟内存(三)堆内存分配及malloc实现原理

RAII妙用之计算函数耗时

羽林君 某嵌入式程序猿分享技术、生活、人生云云文字。如有诗云:去年今日此门中,人面桃花相映红。人面不知何处去,桃花依旧笑春风。
评论 (0)
  • Matter协议是一个由Amazon Alexa、Apple HomeKit、Google Home和Samsung SmartThings等全球科技巨头与CSA联盟共同制定的开放性标准,它就像一份“共生契约”,能让原本相互独立的家居生态在应用层上握手共存,同时它并非另起炉灶,而是以IP(互联网协议)为基础框架,将不同通信协议下的家居设备统一到同一套“语义规则”之下。作为应用层上的互通标准,Matter协议正在重新定义智能家居行业的运行逻辑,它不仅能向下屏蔽家居设备制造商的生态和系统,让设备、平
    华普微HOPERF 2025-05-08 11:40 40浏览
  • ‌一、高斯计的正确选择‌1、‌明确测量需求‌‌磁场类型‌:区分直流或交流磁场,选择对应仪器(如交流高斯计需支持交变磁场测量)。‌量程范围‌:根据被测磁场强度选择覆盖范围,例如地球磁场(0.3–0.5 G)或工业磁体(数百至数千高斯)。‌精度与分辨率‌:高精度场景(如科研)需选择误差低于1%的仪器,分辨率需匹配微小磁场变化检测需求。2、‌仪器类型选择‌‌手持式‌:便携性强,适合现场快速检测;‌台式‌:精度更高,适用于实验室或工业环境。‌探头类型‌:‌横向/轴向探头‌:根据磁场方向选择,轴向探头适合
    锦正茂科技 2025-05-06 11:36 381浏览
  • 随着智能驾驶时代到来,汽车正转变为移动计算平台。车载AI技术对存储器提出新挑战:既要高性能,又需低功耗和车规级可靠性。贞光科技代理的紫光国芯车规级LPDDR4存储器,以其卓越性能成为国产芯片产业链中的关键一环,为智能汽车提供坚实的"记忆力"支持。作为官方授权代理商,贞光科技通过专业技术团队和完善供应链,让这款国产存储器更好地服务国内汽车厂商。本文将探讨车载AI算力需求现状及贞光科技如何通过紫光国芯LPDDR4产品满足市场需求。 车载AI算力需求激增的背景与挑战智能驾驶推动算力需求爆发式
    贞光科技 2025-05-07 16:54 141浏览
  • 2024年初,OpenAI公布的Sora AI视频生成模型,震撼了国产大模型行业。随后国产厂商集体发力视频大模型,快手发布视频生成大模型可灵,字节跳动发布豆包视频生成模型,正式打响了国内AI视频生成领域第一枪。众多企业匆忙入局,只为在这片新兴市场中抢占先机,却往往忽视了技术成熟度与应用规范的打磨。以社交平台上泛滥的 AI 伪造视频为例,全红婵家人被恶意仿冒博流量卖货,明星们也纷纷中招,刘晓庆、张馨予等均曾反馈有人在视频号上通过AI生成视频假冒她。这些伪造视频不仅严重侵犯他人权
    用户1742991715177 2025-05-05 23:08 79浏览
  • 这款无线入耳式蓝牙耳机是长这个样子的,如下图。侧面特写,如下图。充电接口来个特写,用的是卡座卡在PCB板子上的,上下夹紧PCB的正负极,如下图。撬开耳机喇叭盖子,如下图。精致的喇叭(HY),如下图。喇叭是由电学产生声学的,具体结构如下图。电池包(AFS 451012  21 12),用黄色耐高温胶带进行包裹(安规需求),加强隔离绝缘的,如下图。451012是电池包的型号,聚合物锂电池+3.7V 35mAh,详细如下图。电路板是怎么拿出来的呢,剪断喇叭和电池包的连接线,底部抽出PCB板子
    liweicheng 2025-05-06 22:58 284浏览
  • 文/郭楚妤编辑/cc孙聪颖‍相较于一众措辞谨慎、毫无掌舵者个人风格的上市公司财报,利亚德的财报显得尤为另类。利亚德光电集团成立于1995年,是一家以LED显示、液晶显示产品设计、生产、销售及服务为主业的高新技术企业。自2016年年报起,无论业绩优劣,董事长李军每年都会在财报末尾附上一首七言打油诗,抒发其对公司当年业绩的感悟。从“三年翻番顺大势”“智能显示我第一”“披荆斩棘幸从容”等词句中,不难窥见李军的雄心壮志。2012年,利亚德(300296.SZ)在深交所创业板上市。成立以来,该公司在细分领
    华尔街科技眼 2025-05-07 19:25 128浏览
  • 后摄像头是长这个样子,如下图。5孔(D-,D+,5V,12V,GND),说的是连接线的个数,如下图。4LED,+12V驱动4颗LED灯珠,给摄像头补光用的,如下图。打开后盖,发现里面有透明白胶(防水)和白色硬胶(固定),用合适的工具,清理其中的胶状物。BOT层,AN3860,Panasonic Semiconductor (松下电器)制造的,Cylinder Motor Driver IC for Video Camera,如下图。TOP层,感光芯片和广角聚焦镜头组合,如下图。感光芯片,看着是玻
    liweicheng 2025-05-07 23:55 71浏览
  • 5小时自学修好BIOS卡住问题  更换硬盘故障现象:f2、f12均失效,只有ESC和开关机键可用。错误页面:经过AI的故障截图询问,确定是机体内灰尘太多,和硬盘损坏造成,开机卡在BIOS。经过亲手拆螺丝和壳体、排线,跟换了新的2.5寸硬盘,故障排除。理论依据:以下是针对“5小时自学修好BIOS卡住问题+更换硬盘”的综合性解决方案,结合硬件操作和BIOS设置调整,分步骤说明:一、判断BIOS卡住的原因1. 初步排查     拔掉多余硬件:断开所有外接设备(如
    丙丁先生 2025-05-04 09:14 118浏览
  • 二位半 5线数码管的驱动方法这个2位半的7段数码管只用5个管脚驱动。如果用常规的7段+共阳/阴则需要用10个管脚。如果把每个段看成独立的灯。5个管脚来点亮,任选其中一个作为COM端时,另外4条线可以单独各控制一个灯。所以实际上最多能驱动5*4 = 20个段。但是这里会有一个小问题。如果想点亮B1,可以让第3条线(P3)置高,P4 置低,其它阳极连P3的灯对应阴极P2 P1都应置高,此时会发现C1也会点亮。实际操作时,可以把COM端线P3设置为PP输出,其它线为OD输出。就可以单独控制了。实际的驱
    southcreek 2025-05-07 15:06 195浏览
  • 某国产固态电解的2次和3次谐波失真相当好,值得一试。(仅供参考)现在国产固态电解的性能跟上来了,值得一试。当然不是随便搞低端的那种。电容器对音质的影响_电子基础-面包板社区  https://mbb.eet-china.com/forum/topic/150182_1_1.html (右键复制链接打开)电容器对音质的影响相当大。电容器在音频系统中的角色不可忽视,它们能够调整系统增益、提供合适的偏置、抑制电源噪声并隔离直流成分。然而,在便携式设备中,由于空间、成本的限
    bruce小肥羊 2025-05-04 18:14 232浏览
  • 多功能电锅长什么样子,主视图如下图所示。侧视图如下图所示。型号JZ-18A,额定功率600W,额定电压220V,产自潮州市潮安区彩塘镇精致电子配件厂,铭牌如下图所示。有两颗螺丝固定底盖,找到合适的工具,拆开底盖如下图所示。可见和大部分市场的加热锅一样的工作原理,手绘原理图,根据原理图进一步理解和分析。F1为保险,250V/10A,185℃,CPGXLD 250V10A TF185℃ RY 是一款温度保险丝,额定电压是250V,额定电流是10A,动作温度是185℃。CPGXLD是温度保险丝电器元件
    liweicheng 2025-05-05 18:36 257浏览
  • UNISOC Miracle Gaming奇迹手游引擎亮点:• 高帧稳帧:支持《王者荣耀》等主流手游90帧高画质模式,连续丢帧率最高降低85%;• 丝滑操控:游戏冷启动速度提升50%,《和平精英》开镜开枪操作延迟降低80%;• 极速网络:专属游戏网络引擎,使《王者荣耀》平均延迟降低80%;• 智感语音:与腾讯GVoice联合,弱网环境仍能保持清晰通话;• 超高画质:游戏画质增强、超级HDR画质、游戏超分技术,优化游戏视效。全球手游市场规模日益壮大,游戏玩家对极致体验的追求愈发苛刻。紫光展锐全新U
    紫光展锐 2025-05-07 17:07 185浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦