最近在研究设计模式,发现尽管设计模式是针对面向对象语言提出的,但貌似市面上大多数都是基于 java 给出的例子,C++的例子极少,自己看完李建忠老师的 GOF 设计模式视频后查阅各种资料并结合自身实践总结如下,希望对大家有所帮助。
设计模式简介
设计模式是主要针对面向对象语言提出的一种设计思想,主要是提高代码可复用性,抵御变化,尽量将变化所带来的影响降到最低。
面向对象特点
封装:隐藏内部实现
继承:复用现有的代码
多态:改写对象的行为
面向对象设计原则
依赖倒置原则:针对接口编程,依赖于抽象而不依赖于具体,抽象(稳定)不应依赖于实现细节(变化),实现细节应该依赖于抽象,因为稳定态如果依赖于变化态则会变成不稳定态。
开放封闭原则:对扩展开放,对修改关闭,业务需求是不断变化的,当程序需要扩展的时候,不要去修改原来的代码,而要灵活使用抽象和继承,增加程序的扩展性,使易于维护和升级,类、模块、函数等都是可以扩展的,但是不可修改。
单一职责原则:一个类只做一件事,一个类应该仅有一个引起它变化的原因,并且变化的方向隐含着类的责任。
里氏替换原则:子类必须能够替换父类,任何引用基类的地方必须能透明的使用其子类的对象,开放关闭原则的具体实现手段之一。
接口隔离原则:接口最小化且完备,尽量少 public 来减少对外交互,只把外部需要的方法暴露出来。
最少知道原则:一个实体应该尽可能少的与其他实体发生相互作用。
将变化的点进行封装,做好分界,保持一侧变化,一侧稳定,调用侧永远稳定,被调用测内部可以变化。
优先使用组合而非继承,继承为白箱操作,而组合为黑箱,继承某种程度上破坏了封装性,而且父类与子类之间耦合度比较高。
针对接口编程,而非针对实现编程,强调接口标准化。
小总结: 没有一步到位的设计模式,刚开始编程时不要把太多精力放到设计模式上,需求总是变化的,刚开始着重于实现,一般敏捷开发后为了应对变化重构再决定采取合适的设计模式。
模板方法
父类定义算法的骨架,而将一些步骤延迟到子类去实现,使得子类可以复用骨架,并附加特性,以开发框架举例,框架开发人员把框架调用流程定好,而将某些具体的步骤作为虚函数留给子类去重写,话不多说,上代码。
#ifndef GAME
#define GAME
#include <iostream>
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 game
start basketball game
step 3: stop game
step 1: init game
start socketball game
step 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 <iostream>
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 <iostream>
class ObserverBase {
public:
ObserverBase() {}
virtual ~ObserverBase() {}
virtual void Update() {}
};
#endif
复制代码
#ifndef _OBSERVERFIRSTCHILD
#define OBSERVERFIRSTCHILD_
#include "observer.h"
class ObserverFirstChild : public ObserverBase {
void Update() override {
std::cout << "first child receive notify" << std::endl;
}
};
#endif
复制代码
#ifndef _OBSERVERSECONDCHILD
#define OBSERVERSECONDCHILD_
#include "observer.h"
class ObserverSecondChild : public ObserverBase {
void Update() override {
std::cout << "second child receive notify" << std::endl;
}
};
#endif
复制代码
#include <list>
#include "observerfirstchild.h"
#include "observersecondchild.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<ObserverBase > 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 <iostream>
class Game {
public:
Game() {}
virtual ~Game() {}
virtual void Skill() { std::cout << "game skill" << std::endl; }
};
#endif
复制代码
定义卡丁车子类和 lol 子类
#ifndef _CARGAME
#define CARGAME_
#include "game.h"
class CarGame : public Game {
public:
void Skill() override { std::cout << "car game" << std::endl; }
};
#endif
复制代码
#ifndef _LOLGAME
#define LOLGAME_
#include "game.h"
class LolGame : public Game {
public:
void Skill() override { std::cout << "lol game" << std::endl; }
};
#endif
复制代码
篮球和足球子类也可以像上面一样继承定义,但是如果都像上面一样继承的话不能实现动态扩展功能的需求,所以先定义一个装饰类,之后定义继承这个装饰类的篮球和足球子类。
#ifndef _DECORATORGAME
#define DECORATORGAME
#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 "lolgame.h"
#include "decoratorgame.h"
#include "basketball.h"
#include "cargame.h"
#include "socketball.h"
int main() {
Game lol = new LolGame();
Game car = new CarGame();
// 一个人既会打篮球又会打LOL可以如下使用
Game basketballlol = new BasketBallGame(lol);
basketballlol->Skill();
std::cout << std::endl;
// 一个人既会打篮球又会玩卡丁车可以如下使用
Game basketballcar = new BasketBallGame(car);
basketballcar->Skill();
std::cout << std::endl;
// 一个人既会打篮球又会玩卡丁车又会踢足球可以如下使用
Game* g = new SocketBallGame(basketballlol);
g->Skill();
delete lol;
delete basketballcar;
delete basketballlol;
delete g;
return 0;
}
// 输出
basketball game
lol game
basketball game
car game
socket game
basketball game
lol game
复制代码
通过上例可以看出,使用观察者模式可以动态的扩展类的职责,动态的组合类的各个功能,当看代码时发现一个类既继承了父类同时又持有父类的对象指针,那这基本上就是装饰器模式。
桥接模式
确定好抽象部分和实现部分,将抽象部分和实现部分分离,使得他们可以独立的变化,实现系统和抽象系统可能各自都会有各自的变化,使用桥接模式可以更加灵活的扩展,方法:用组合代替继承。
举例:一个图形基类,可以有圆形矩形多边形等等,每个都需要从图形基类中继承出一个子类,但是每个子类还需要画图,画颜色,圆形可能需要黄色黑色白色蓝色等等,矩形也可能需要黄色黑色白色蓝色等等,这种如果单纯的使用继承就不太灵活,可以使用桥接模式,把图形种类当作抽象部分,画颜色当作实现部分,使用组合的方式将抽象部分和实现部分分离,上代码:
首先将实现部分(画图)提取出来,同时多种画图方式从这个基类继承后重写,之后会作为 Shape 图形类的一个成员变量
#ifndef _SHAPEDRAWIMPL
#define SHAPEDRAWIMPL_
#include <iostream>
class ShapeDrawImpl {
public:
virtual void Draw() = 0;
virtual ~ShapeDrawImpl() {}
};
#endif
复制代码
#ifndef _SHAPEDRAWIMPLRED
#define SHAPEDRAWIMPLRED
#include "shapedraw_impl.h"
class ShapeDrawImplRed : public ShapeDrawImpl{
public:
virtual void Draw() override {
std::cout << "draw red" << std::endl;
}
};
#endif
复制代码
#ifndef _SHAPEDRAWIMPLBLACK
#define SHAPEDRAWIMPLBLACK
#include "shapedraw_impl.h"
class ShapeDrawImplBlack : public ShapeDrawImpl{
public:
virtual void Draw() override {
std::cout << "draw black" << std::endl;
}
};
#endif
复制代码
上面定义了两种颜色的实现方式,红色和黑色,下面定义图形的类,定义一个图形基类,持有画图实现的句柄,之后定义多个继承图形基类的子类,圆形子类和矩形子类。
#ifndef SHAPE
#define SHAPE
#include "shapedrawimpl.h"
class Shape {
protected:
ShapeDrawImpl impl;
public:
virtual void Update() {}
Shape(ShapeDrawImpl impl) : impl(impl) {}
};
#endif
复制代码
#ifndef _SHAPECIRCLE
#define SHAPECIRCLE_
#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 _SHAPERECTANGLE
#define SHAPERECTANGLE_
#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 "shapecircle.h"
#include "shaperectangle.h"
#include "shapedrawimplblack.h"
#include "shapedrawimplred.h"
int main() {
ShapeDrawImpl impl = new ShapeDrawImplBlack();
Shape circle = new ShapeCircle(impl);
circle->Update();
delete impl;
delete circle;
return 0;
}
输出:
circle shape update
draw black
复制代码
通过桥接模式可以更好的应对变化,应对抽象和实现的多种组合变化。
工厂模式
工厂模式属于创建型模式,主要用于创建对象时不向外部暴露创建逻辑,通过一个共同的接口指向新创建的对象,通过面向对象的多态,将创建对象的工作延迟到子类执行,由子类决定实例化哪个对象。用于隔离对象使用者与其具体类型之间的耦合关系,当具体类型经常变化时,可以考虑使用工厂模式。有一个类型的抽象基类,同时又有很多继承该抽象基类的具体类型,我们做的就是使其依赖于抽象而不依赖于具体,实现方式是创建一个工厂基类,在为每个具体类型定义一个可以创建其相应对象的工厂,每一个具体类对应一个具体工厂,工厂类继承自工厂基类,通过工厂基类的多态性就可以决定创建什么类型的对象。上代码:
#ifndef GAME
#define GAME
#include <iostream>
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 _GAMEFACTORY
#define GAMEFACTORY_
#include "game.h"
class GameFactory {
public:
GameFactory() {}
virtual ~GameFactory() {}
virtual Game* CreateGame() = 0;
};
#endif
复制代码
#ifndef _BASKETBALLFACTORY
#define BASKETBALLFACTORY
#include "gamefactory.h"
#include "basketball.h"
class BasketBallFactory : public GameFactory {
public:
Game* CreateGame() override{
return new BasketBall();
};
};
#endif
复制代码
#ifndef _SOCKETBALLFACTORY
#define SOCKETBALLFACTORY
#include "gamefactory.h"
#include "socketball.h"
class SocketBallFactory : public GameFactory {
public:
Game* CreateGame() override{
return new SocketBall();
};
};
#endif
复制代码
#include "basketballfactory.h"
#include "socketballfactory.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 basketball
play socketball
复制代码
当有新类型增加时,需要添加一个具体类和一个相应的创建工厂,尽管减少了耦合度,但是其实还是比较麻烦的。
抽象工厂方法模式
和工厂方法模式类似,不做过多介绍,说一下定义,抽象工厂方法模式主要提供一个接口,让该接口负责创建多系列“相关或相互的对象”,无需指定具体的类,系列对象指的是某一特定系列下的对象间有相互依赖或相互作用的关系,不同系列的对象之间不能相互依赖,如果没有多系列对象创建的需求变化,没必要使用抽象工厂方法模式,使用简单工厂方法模式就可以,拿上一个举例继续说,游戏类型是一个系列,我们有了一个游戏类型的工厂,以后可能会再加入娱乐圈类型的一个系列,那就在做一个娱乐圈类型的系列工厂,以后可能还有文艺类型的系列,那就再加一个文艺类型的系列工厂,这就有了三个系列的工厂,所以就可以在这三类工厂的基础上再抽象出一个抽象的超级工厂,根据不同需求选择实例化哪一个系列的具体工厂,再创建具体工厂下的具体类型的对象。
原型模式
用于创建重复的对象,定义一个 clone 接口,通过调用 clone 接口创建出与原来类型相同的对象,上代码:
#ifndef GAME
#define GAME
#include <iostream>
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 newgame = game->clone();
newgame->Play();
delete game;
delete new_game;
return 0;
}
复制代码
单纯看 game 不知道它是什么类型,它可能是篮球游戏也可能是足球游戏等,如果想创建一个与它相同类型的对象就可以使用原型模式其实就是实现一个 clone 接口,如果一个对象的拷贝构造函数比较复杂而自己不想使用拷贝构造的方式创建对象也可以使用原型模式,使用方式见上例。
建造者模式
用于构建一个复杂的大的对象,一个复杂的对象通常需要一步步才可以构建完成,建造者模式强调的是一步步创建对象,并通过相同的构建过程可以获得不同的结果对象,一般来说建造者对象不是直接返回的,与抽象工厂方法区别是抽象工厂方法用于创建多个系列的对象,而建造者模式强调一步步构建对象,并且构建步骤固定,举例:想要构建一个老师类的对象,老师有第一技能和第二技能,第一技能是数学就代表是数学老师,第一技能是英语就代表是语文老师,构造一个老师对象需要先设定老师的第一技能再设定老师的第二技能,强调一步步设定技能,将这一步步的构建过程可以抽象出建造者模式。
首先定义老师类
#ifndef TEACHER
#define TEACHER
#include <iostream>
#include <string>
class Teacher {
private:
std::string firstskill;
std::string secondskill;
public:
void SetFirstSkill(const std::string& skill) { this->firstskill = skill; }
void SetSecondSkill(const std::string& skill) { this->secondskill = skill; }
};
#endif
复制代码
定义一个老师的抽象构建器,再根据具体老师继承出具体的老师构建器
#ifndef _TEACHERABSTRACTBUILDER
#define TEACHERABSTRACTBUILDER_
#include "teacher.h"
class TeacherAbstractBuilder {
public:
TeacherAbstractBuilder() {}
virtual ~TeacherAbstractBuilder() {}
virtual void BuildFirstSkill() = 0;
virtual void BuildSecondSkill() = 0;
virtual Teacher* GetTeacher() = 0;
};
#endif
复制代码
#ifndef _MATHTEACHERBUILDER
#define MATHTEACHERBUILDER
#include "teacherabstractbuilder.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 _ENGLISHTEACHERBUILDER
#define ENGLISHTEACHERBUILDER
#include "teacherabstractbuilder.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 "teacherabstractbuilder.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 "englishteacherbuilder.h"
#include "mathteacherbuilder.h"
int main() {
TeacherAbstractBuilder builder = new MathTeacherBuilder();
Director director = new Director(builder);
delete builder;
delete director;
builder = new EnglishTeacherBuilder();
director = new Director(builder);
delete builder;
delete director;
return 0;
}
复制代码
通过 Director 利用不同的具体构建器都采用相同的步骤一步步构建出不同的具体的老师类对象。
单例模式
不多说了,网上太多了,列一段单例的代码:
static SingleTon& GetInstance() {
static SingleTon t;
return t;
}
复制代码
享元模式
通过共享技术有效支持大量细粒度的对象,主要解决面向对象代价问题,通过共享有效降低创建的对象个数,类似于对象池。
举例:篮球分多种颜色,我想要蓝色的篮球想要红色的篮球这就需要创建两个对象,当我再想要一个蓝色的篮球如果能够继续使用之前的那个对象就更好,通过享元模式可以做到。
#ifndef BASKETBALL
#define BASKETBALL
#include <iostream>
#include <string>
class BasketBall {
public:
BasketBall(const std::string& color) : color(color) {}
~BasketBall() {}
private:
std::string color;
};
#endif
复制代码
#ifndef _BASKETBALLFACTORY
#define BASKETBALLFACTORY
#include <unorderedmap>
#include "basketball.h"
class BasketballFactory {
private:
std::unorderedmap<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 whiteball1 = factory.GetBasketBallWithColor("white");
BasketBall blackball1 = factory.GetBasketBallWithColor("black");
BasketBall whiteball2 = factory.GetBasketBallWithColor("white");
BasketBall blackball2 = factory.GetBasketBallWithColor("black");
std::cout << whiteball1 << std::endl;
std::cout << whiteball2 << std::endl;
std::cout << blackball1 << std::endl;
std::cout << blackball2 << std::endl;
// remember to delete
return 0;
}
输出:
0x7fffe4984e70
0x7fffe4984e70
0x7fffe4984f00
0x7fffe4984f00
复制代码
外观模式(Facade Pattern)
感觉外观模式没啥特别的,感觉就是做好功能之间边界的划分,做好封装,弄清楚哪部分是稳定的,哪部分是变化的,对外稳定松耦合,对内迭代变化高内聚,子系统的内部外部要做好解耦,为子系统的一组接口提供一个稳定一致的(界面),子系统中的任何变化都不会影响这个(界面)的变化。
代理模式(Proxy Pattern)
为其它对象提供一种代理以控制这个对象的访问,一般对于比较大的对象访问比较困难或者或带来很多麻烦,例如裸指针的使用,所以一般使用智能指针来控制裸指针,使用智能指针也是一种代理模式,举例:A 喜欢 B 想送给 B 礼物,但是却不认识 B,而 C 却认识 B,所以可以把 C 作为 A 的代理去送给 B 礼物,代码:
#ifndef GIRL
#define GIRL
#include <iostream>
#include <string>
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 _GIFTPROXY
#define GIFTPROXY
#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;
}
复制代码
如上述代码,通过代理模式就可以给丽丽送礼物。
适配器模式
太常见了,每个人都会用,两个不兼容的接口之间的桥梁,就像耳机转换头,充电器转换头等等都是适配器模式,将一个类的接口转换为客户希望的另一种接口的形式,使得原本由于接口不兼容而不能一起工作的类可以一起工作。
中介模式
使用一个中介对象来封装一系列的对象交互,当多个对象间互相引用且操作比较复杂时可以考虑使用中介模式。如下图所示:左侧五个对象互相依赖,通过中介这个桥梁就可以减少这个依赖。
![d320c85de4434586e5fb5931742e6cd.jpg](https://upload-images.jianshu.io/upload_images/3725930-0dc4b653116db321.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/440)
状态模式
当一个对象的行为依赖于它的状态并且其有很多种状态而且将来还会有更多状态时,如果使用简单的 if-else 来增加新状态就违反了面向对象的开闭原则,这时可以考虑使用状态模式,将具体的状态做出一个抽象类,也类似于工厂模式,将具体的状态分散于各个子类中,避免了更多的 if-else 分支,上代码:
#include <iostream>
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;
}
复制代码
评论