设计模式13-代理模式
代理模式与装饰器模式的区别在于,一个是”加料”,一个是”访问控制” 我是员工,想找老板,老板时间很宝贵,所以老板推出一个秘书,有什么事先告诉秘书,秘书先过滤一下访客,真正需要老板决定的再转告老板,这就是一种代理 另一个例子,在网络通讯中,存在两种代理:正向代理和反向代理,当client需要访问server的服务时,如果client在自己后面加一层代替自己访问server,server的结果只会反馈给加的这层,它感知不到背后真正访问的client,这就叫正向代理;如果server在自己前面加一层,client的请求先到加的这层,它感知不到背后真正在工作的server是谁,这就叫反向代理 以DNS查询为例: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970#include <iostream>#include <memory>#include &...
设计模式12-享元模式
抠门是一种艺术 在很多游戏场景中,会有一大片草地,如果每根草都是一个对象,我们需要给它一个显示图片,很自然的写下如下代码 1234567891011class Grass {public: void show() { img = new Image("img/grass.png"); opengl::show(x, y, img); }private: int x; int y; Image* img;} 草越多,占用的内存越多,但是他们又长得一样,我何必每个对象里都new一个,大家共享一个它不香吗? 于是乎我们这么改,把可以变化的座标x,y保留,把不会变化的img拆分出去 123456789class Grass {public: void show(Image* img) { opengl::show(x, y, img); }private: int x; int y;} ...
设计模式11-外观模式
突然发现,外观模式好像比单例还要简单,以至于我们经常用,都没注意到过它的存在 它的思想就是把复杂关进房间里,让外部保持干净 比如我们有个类Worker 123456789101112class Worker {public: void process1() { } void process2() { } void process3() { }} 之前都是靠程序员我们自己去记住,让它工作需要先调process1,再调process2,最后调process3,后来我们稍微改下 123456789101112131415161718class Worker {public: void start() { process1(); process2(); process3(); }private: void process1() { ...
设计模式10-装饰器模式
装饰器的核心是“加料”,我往你身上“套”功能,但你还是你,外界并不知道你被套了 就像浏览器发送请求一样,我只需要拿到一个StreamProcesser,把我想发出去的内容交给它即可,至于它被依次套上了Html标签,经过TLS加密,还是IP层的封装,我不关心 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253#include <cstddef>#include <iostream>#include <string>#include <memory>class StreamProcesser {public: virtual void process(std::string input) = 0; virtual ~StreamProcesser() {} StreamProcesser(std::shared_ptr<StreamPro...
设计模式9-组合模式
从文件系统、UI 树到宏命令,聊聊如何用统一接口抹平‘整体’与‘部分’的差异,让递归逻辑在对象内部自然生长。 你是个将军。 这是个假设。 但请你带入这个角色。 你想发起一场总攻。 你手下有 3 个师。 每个师有 4 个旅。 每个旅有 5 个团。 一直到最底层的列兵。 如果你的代码逻辑是这样的: 1234567891011void General::attack() { for (auto& division : divisions) { for (auto& brigade : division.brigades) { for (auto& regiment : brigade.regiments) { // ... 写了五层循环 // 终于找到士兵 soldier.fire(); } } }} 那你...
设计模式8-桥接模式
假设我们有M个类,分别可以使用N个类所提供的服务,难道我们需要在M个类中实现N个函数吗? 抑或是通过冗长的if-else来判断使用的是N中哪个?这样做复杂度是MxN。我们不妨为M提供一个统一的基类,为N提供一个统一的基类,让两个基类互相关联,这样我们就将复杂度降到了M+N,就好像河东的M个村庄都到桥东,河西的N个村庄都到桥西,大家仅靠一座桥即可完成沟通,这就是桥接模式。 代码中我们这么设计一个场景,比如我们有3个设备类:笔记本,台式机,手机,又有两个标点外设类:鼠标,触摸板,如果让三个设备都可以分别使用两个外设,我们不需要每个设备类里都实现两个外设的调用,只需要把设备提升出一个基类Device, 外设提升出一个基类PointerDevice,把PointerDevice传入Device,Device调用对应的接口即可,具体的设备无需关新到底是哪个外设 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626...
设计模式7-适配器模式
适配器模式简单来说,就是提供服务方,与你想使用的接口不匹配,所以把提供服务方包一层,转为你想使用的接口 比如我们有一个手机类,它期望连接一个TypeC接口的对象,但是我们有一个USB的U盘,如何让U盘给手机提供服务呢,这时我们就需要一个适配器类USBadaptoTypeC,它把U盘封装一层然后提供一个TypeC格式的接口 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758#include <cstddef>#include <iostream>#include <string>#include <vector>class TypeCInterface {public: virtual std::vector<std::string> query() = 0; virtual ~TypeCInterface(){};...
设计模式6-原型模式
当我们有一个对象时,想基于该对象生成一个一模一样的对象,有人说,那我直接拷贝构造一个不就行了,专门搞个设计模式岂不是脱裤子放屁?确实,比如我有一个Cat c = new Cat;想再造一个直接拷贝构造即可,但是如果我用 Abstruct Animal = new Cat; 我只有一个抽象类的指针却想生成一个一模一样的猫,貌似拷贝构造就解决不了问题了 再举个例子,比如画图工具,一般都会提供右键选中已有图形,复制一份的功能,但是如果这个图形只是一个抽象类的指针,应该如何复制出一个一样的形状? 我们设计一个抽象的类shape,提供一个纯虚函数clone,它的返回类型为shape* ,每个继承它的类,无论是圆形还是方形必须重写该方法,当我们拿到一个shape*指针时,无论它实际是个圆还是个方形,只要调用clone一定可以获取一个一模一样的对象 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960#in...
设计模式5-建造者模式
建造者模式主要为解决一个问题,当构造一个对象本身变得复杂时,如果又有另一个类似的对象需要构造,难道需要程序员自己清楚的记住应该先干嘛后干嘛然后获得一个对象吗?能不能把构造流程标准化?标准化的流程顺带解决了另一个问题,它可以防止未构造完成的”半成品”流入客户手中产生未知后果 比如我们需要建造一座房子,一定是从下往上建造,依次完成地面,墙体,以及屋顶。无论是木屋还是石头屋都要遵守这个流程,先设计一个抽象的Builder类,它有建造地面,墙体,屋顶的方法,并提供一个最后的获取产品的接口build,用WoodHouseBuilder和StoneHouseBuilder来继承它,分别实现建造各个组件的方法,最后我们设计一个director,让他指导Builder将建造流程标准化(先建造地面,然后墙,然后屋顶),最后我们即可从Builder的build接口获取到一个成品房子,绝对不会拿到一个半成品 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535...
设计模式4-抽象工厂
与前面两个工厂不同的是,抽象工厂提供的产品不是同一个类型的东西,而是一系列配套的不同产品。例如一个网站可能会有Light theme和Dark theme,对应不同的皮肤skin,按钮button,搜索栏searchbar。如何在装配时防止出现Light的skin和button但是搭配上了Dark的searchbar,即是抽象工厂要解决的问题 我们定义一个最终产品类叫做page,page里包括了skin,button,searchbar。然后给出Light和Dark分别对这三个组件的实现,设计一个抽象工厂类,实现LightFactory和DarkFactory来装配page并返回指针 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103...









