作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

设计模式是为了解决在软件开发过程中遇到的某些问题而形成的思想。同一场景有多种设计模式可以应用,不同的模式有各自的优缺点,开发者可以基于自身需求选择合适的设计模式,去解决相应的工程难题。
良好的软件设计和架构,可以让代码具备良好的可读性、可维护性、可扩展性、可复用性,让整个系统具备较强的鲁棒性和性能,减少屎山代码出现的概率。
想要熟练运用设计模式,提高自己的编程能力和架构能力,只有在自己工作中,结合自身工作内容,多思考多实践。本文只能通过举一些通俗的例子,来帮助你更快或者说更容易地去理解设计模式的一些底层逻辑,光看可不行哦,一定要多动手实操,看完本文就去实践吧。
该原则:对一个类而言,其职责应该只有一个,即只有一个可以引起其改变的原因。
若类承担的职责过多,其内部耦合性就大大增加,某个职责的变化会带给其他职责不可预估的影响,这样设计会使得类相对脆弱。
通俗地理解就是,你如果在当保安,就不要老想着帮清洁打扫卫生,你的领导可能会因为你打扫卫生夸你,但更可能会因为你保安工作没做到位而骂你。
该原则:面向对象编程领域中规定,软件中的对象应该对于扩展是开放的,对于修改是封闭的,即一个实体允许在不改变它原有源代码前提下变更其行为。
开闭原则可以使我们的软件具备较优的鲁棒性,代码更模块化且利于扩展和维护,从而提高开发效率。经常开发的工程师都清楚,在一个旧的系统中添加新的功能是很不容易的,如果你添加功能要大量修改原有的代码内容,大概可能出现如下几类情况:
综上来看,如果系统有良好的设计,也尽可能满足了开闭原则,那么后续在扩展新功能时,原有系统不会受到影响,只需要针对新扩展的部分重点测试即可(测试工程师狂喜),即便是扩展的功能出现了问题,也相对容易进行修复,最坏就是把扩展的内容全删掉罢了。
该原则:任何基类可以出现的地方,子类一定可以出现。
该原则是继承复用的基石,只有子类能完全替换基类,且功能不受影响,基类才算是真正被复用。想满足该原则,一般来说就尽量从抽象类继承,而不是从具体类继承,还可以根据如下4点来判断:
里氏代换原则可以很大程度降低代码出问题的概率,也是一个良好的设计和编程习惯。
该原则:程序要依赖于抽象接口,不要依赖于具体实现,对抽象编程,不要对实现编程,这样降低了客户端和实现模块间的耦合。一般情况抽象变化的概率小,依赖于抽象,即使实现细节一直变化,也不影响客户端。
想遵循该原则,尽量满足如下4点要求:
依赖倒置原则的目的就是实现解耦,高层模块不再依赖于具体实现。
该原则:也称为迪米特原则,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果需要调用另一个对象的某个方法,可以通过引入一个合理的第三者来调用,从而降低他们之间的耦合度。
想遵循该原则,尽量满足如下3点要求:
最小知识原则尽可能地减少了对象间的联系,保持功能模块相对独立,降低依赖。
该原则:在一个新的对象中使用已有的对象,使之成为新对象的一部分,通过委派调用已有对象的方法达到复用目的,应优先采用合成/聚合的方式,其次采用继承方式。
通过合成/聚合复用的优势:
应用合成复用原则,不要放松对嵌入对象的管理,尤其是嵌入对象过多时,这很考验开发者的实力。
3.1-3.5是创建型模式,3.6-3.12是结构型模式,3.13-3.23是行为型模式。
单例模式是一种创建型的软件设计模式,在工程项目中非常常见。通过单例模式的设计,使得创建的类在当前进程中只有一个实例,并提供一个全局性的访问点,这样可以规避因频繁创建对象而导致的内存飙升情况。
实现单例模式的三个要点:
1)私有化构造函数:这样外界就无法自由地创建类对象,进而阻止了多个实例的产生。
2)类定义中含有该类的唯一静态私有对象:静态变量存放在全局存储区,且是唯一的,供所有对象使用。
3)用公有的静态函数来获取该实例:提供了访问接口。
单例模式一般分为懒汉式和饿汉式。
1)懒汉式:在使用类对象(单例实例)时才会去创建它,不然就懒得去搞。
2)饿汉式:单例实例在类装载时构建,有可能全局都没使用过,但它占用了空间,就像等着发救济粮的饿汉提前排好队等吃的一样。
实现单例模式通常面临两个问题,线程安全和内存泄漏。针对线程安全,可通过双重检测锁的方式来解决;针对内存泄漏,可通过资源管理的方式来解决,如智能指针和静态嵌套类。
具体代码实现详情见:
设计模式之单例模式(C++)_翟天保Steven的博客-CSDN博客
工厂模式是一种创建型的软件设计模式。定义一个用于创建对象的工厂接口,并让工厂子类决定实例化哪一个产品类,使产品类的实例化延迟到工厂子类中执行。说白了就是用来造东西的,一般是比较简单的东西,我们不需要知道它如何生产的,直接从工厂拿到产品即可。
工厂模式的优点:
工厂模式的缺点:
举例:水果工厂生产,具体代码实现详情见:
设计模式之工厂模式(C++)_翟天保Steven的博客-CSDN博客

抽象工厂模式是一种创建型的软件设计模式,该模式相当于升级版的工厂模式。
如果说工厂模式对应一个产品系列,那抽象工厂就对应了多个产品系列。比如工厂模式中有鞋子、衣服和裤子可以生产,那抽象工厂模式就会衍生出耐克工厂和阿迪工厂,这两个工厂分别生产各自品牌的鞋子、衣服和裤子,客户只需要选择具体工厂和想要的产品即可。如果想要替换产品系列,也只需要将具体工厂切换为别的品牌就行了。
抽象工厂模式的优点:
抽象工厂模式的缺点:
举例:不同国家水果工厂生产,具体代码实现详情见:
设计模式之抽象工厂模式(C++)_翟天保Steven的博客-CSDN博客

建造者模式是一种创建型的软件设计模式,用于构造相对复杂的对象。
建造者模式可以将复杂对象的构建与它的表示分离,使得相同的构建过程可以得到不同的表示。如果说工厂模式和抽象工厂模式更注重产品整体,那建造者模式则更在乎产品的组成和细节。
建造者模式的优点:
建造者模式的缺点:
举例:生产电脑,具体代码实现详情见:
设计模式之建造者模式(C++)_翟天保Steven的博客-CSDN博客

原型模式是一种创建型的软件设计模式,通俗的来讲就是复制粘贴。
通过一个原型对象,快速地创建出多个一致的对象,并对其进行相关的操作。比如文件夹中存放了一个Word文件,你把文件复制了一个副本出来,原件不动,对副本进行修改以达到自己的目的。原型像是一个模板,你可以基于它复制好多对象,而复制出来的副本产生任何变化都不会影响到原型(注意:前提是clone的实现要满足深拷贝)。
原型模式的优点:
原型模式的缺点:
举例:文件复制粘贴,具体代码实现详情见:
设计模式之原型模式(C++)_翟天保Steven的博客-CSDN博客

适配器模式是一种结构型的软件设计模式,也称包装模式,即将相对复杂的功能(可能用到多个类)封装起来,提供一个使用者想要的接口,使用者只需要调用接口,不需要知道接口里封装的内容是如何实现的。
个人工作中经常能用到适配器模式,比如在面对一些第三方库或者SDK开发时,它们的接口往往与我们自己想要的接口不一致,此时适配器模式可以很好地扮演一个接口转换器的角色,将别人的接口与我们的接口对应上。
适配器模式的优点:
适配器模式的缺点:
举例:调用第三方相机SDK,具体代码实现详情见:
设计模式之适配器模式(C++)_翟天保Steven的博客-CSDN博客
类适配器模式:

对象适配器模式:

桥接模式是一种结构型的软件设计模式,将抽象部分与实现部分分离,使他们可以独立地变化。
举例来说,黑色钢笔、红色油笔、红色钢笔等等,如果颜色和笔类型合起来考虑,那类的复杂度将难以想象,若有10个颜色,10个笔类型,那要有10*10个类来涵盖所有类型的笔。但是如果拆分颜色和笔类型,通过组合的形式获得目标,那只要10+10个类即可。这样设计极大降低了系统复杂度和耦合程度。
桥接模式的优点:
桥接模式的缺点:
举例:画笔绘画,具体代码实现详情见:
设计模式之桥接模式(C++)_翟天保Steven的博客-CSDN博客

组合模式是一种结构型的软件设计模式,将对象组合成树形结构,以凸显“部分-整体”的层次结构,使客户端对单个对象和组合对象的操作具备一致性。
组合模式和桥接模式都应用了组合的思想,不同之处在于:桥接模式侧重于同级别间的组合,如多个属性的组合,避免了类爆炸;组合模式侧重于部分和整体的组合,避免了单对象和组合对象的区别对待,那样会增加程序复杂度。
组合模式的优点:
组合模式的缺点:
举例:文件系统,具体代码实现详情见:
设计模式之组合模式(C++)_翟天保Steven的博客-CSDN博客
透明式组合模式:

安全式组合模式:

装饰器模式是一种结构型的软件设计模式,在不改变原类文件或使用继承的前提下,动态地扩展一个对象,进而达到增强或者增加对象功能的目的。
装饰器模式的优点:
装饰器模式的缺点:
举例:炒菜加料,具体代码实现详情见:
设计模式之装饰器模式(C++)_翟天保Steven的博客-CSDN博客

门面模式是一种结构型的软件设计模式,也叫外观模式,它提供了统一的接口去访问多个子系统的接口。举个例子,一个餐馆里有许多角色,每个角色就是一个子系统,餐馆就是总系统,客人来餐馆只需要按要求点餐,不需要管餐馆是怎么运作的。
门面模式的优点:
门面模式的缺点:
举例:餐馆吃饭,具体代码实现详情见:
设计模式之门面模式(C++)_翟天保Steven的博客-CSDN博客

代理模式是一种结构型的软件设计模式,在不改变原代码前提下,提供一个代理,以控制对原对象的访问。
代理模式的优点:
代理模式的缺点:
举例:游戏代理,具体代码实现详情见:
设计模式之代理模式(C++)_翟天保Steven的博客-CSDN博客

享元模式是一种结构型的软件设计模式,通过共享对象的方式,尽可能减少内存占用,从而达到优化的目的。
就像打麻将,同时有10桌在玩,每桌都有4个"八筒",如果建立40个"八筒"对象,那就非常冗余,但如果用享元模式建立一套麻将牌,每桌打出"八筒"时,就调用享元中的"八筒",相当于只用了1个对象,这样即节省了资源,也完成了需求。
上述例子中,桌号和牌号就是享元模式的外蕴状态,如A1八筒,就是A桌的第一个"八筒",A和1是外蕴状态;而卡牌"八筒"本身就是内蕴状态,内蕴是可以共享的。外蕴随环境变化,占用资源也少的多,往往只是简单的数据结构。
享元模式的优点:
享元模式的缺点:
举例:模拟打牌,具体代码实现详情见:
设计模式之享元模式(C++)_翟天保Steven的博客-CSDN博客

策略模式是一种行为型的软件设计模式,针对某个行为,在不同的应用场景下,有不同的实现算法,并且可以互相替换。比如两军交战,军队会采用不同的阵法、策略、兵法应对不同的战况。
策略模式的优点:
策略模式的缺点:
举例:军队策略,具体代码实现详情见:
设计模式之策略模式(C++)_翟天保Steven的博客-CSDN博客

模板模式是一种行为型的软件设计模式,在父类中定义了一个模板算法,只实现模板中的公共部分,将可变部分放在子类中实现,不同的子类对同一模板有不同的扩展和实现。
模板模式的优点:
模板模式的缺点:
举例:安装电脑,具体代码实现详情见:
设计模式之模板模式(C++)_翟天保Steven的博客-CSDN博客

命令模式是一种行为型的软件设计模式,行为请求者通过发起命令,使得行为实现者执行命令要求的行为。通常行为请求者和行为实现者之间是强耦合的,而命令模式能很好地将其解耦。
命令模式的优点:
命令模式的缺点:
举例:军队指挥,具体代码实现详情见:
设计模式之命令模式(C++)_翟天保Steven的博客-CSDN博客

迭代器模式是一种行为型的软件设计模式,提供一种方法能顺序访问聚合对象中的各个元素,而又不暴露其内部。
我们使用的聚合对象各种各样,比如vector、list、tree、map等等,既然是聚合,那就有访问其个体的需要。而遍历访问这个行为可能有深度优先、广度优先、顺序遍历、逆序遍历等等,迭代器的意义就是将这个行为抽离封装起来,这样客户端只需要调用合适的迭代器,来进行对应的遍历,而不用自己去实现这一行为。
迭代器模式的优点:
迭代器模式的缺点:
举例:容器遍历,具体代码实现详情见:
设计模式之迭代器模式(C++)_翟天保Steven的博客-CSDN博客

中介者模式是一种行为型的软件设计模式,也称为仲裁者模式,顾名思义,该模式的作用就是中介,帮助其他类进行良好的交流。
类之间关系混乱的时候,会有很强的耦合性。就拿租房为例,如果有10个租客,5个房东,他们各自单线联系,那么他们之间的交流会相对混乱,维护起来也复杂的多。此时出现了一个房产中介,它将租客和房东的信息整合起来,由中介安排合适的租客和房东交流,那么交流就变得高效且清晰,当然也要付费的(就像增加中介类的代码维护开销一样)。
中介者模式的优点:
中介者模式的缺点:
举例:房屋租赁,具体代码实现详情见:
设计模式之中介者模式(C++)_翟天保Steven的博客-CSDN博客

观察者模式是一种行为型的软件设计模式,定义对象间的一种一对多的依赖关系,当被观察者状态发生改变时,所有观察者都做出相应改变。Qt中的信号与槽就是一种典型的观察者模式。
观察者模式的优点:
观察者模式的缺点:
举例:开车等红绿灯,具体代码实现详情见:
设计模式之观察者模式(C++)_翟天保Steven的博客-CSDN博客

备忘录模式是一种行为型的软件设计模式,在不破坏封装的前提下,获取一个对象的内部状态,并在对象外保存该状态,当对象需要恢复到该状态时,对其进行恢复。
备忘录模式的优点:
备忘录模式的缺点:
举例:存读游戏进度,具体代码实现详情见:
设计模式之备忘录模式(C++)_翟天保Steven的博客-CSDN博客

状态模式是一种行为型的软件设计模式,当一个对象的内在状态改变时,其行为也随之改变。就像玩游戏的时候,不同的buff状态,角色会有不同的伤害、技能等等。
当控制一个对象状态的条件表达式过于复杂时,很适合用该模式,将复杂的判断逻辑转移到表示不同状态的系列类中,能将逻辑大大简化。
状态模式的优点:
状态模式的缺点:
举例:开关灯,具体代码实现详情见:
设计模式之状态模式(C++)_翟天保Steven的博客-CSDN博客

责任链模式是一种行为型的软件设计模式,对象内存在对下家的引用,层层连接形成了一条责任链,请求的信息在链上传递直到某个对象决定处理该信息。
责任链模式的优点:
责任链模式的缺点:
举例:申请批假,具体代码实现详情见:
设计模式之责任链模式(C++)_翟天保Steven的博客-CSDN博客

访问者模式是一种行为型的软件设计模式,表示一个作用于某对象结构中的各元素的操作。使得在不改变各元素类的前提下,能定义作用于这些元素的操作。
该模式适合数据结构相对稳定且算法又易变化的系统。数据结构是被访问者,算法操作相当于访问者。
访问者模式的优点:
访问者模式的缺点:
举例:市长视察学校和企业,具体代码实现详情见:
设计模式之访问者模式(C++)_翟天保Steven的博客-CSDN博客

解释器模式是一种行为型的软件设计模式,定义了一个解释器,来解释给定语言和文法的句子。也可以理解为翻译吧,比如1+1,翻译为一加上一,等于二,这样就做成了一个简单的加法计算器。
解释器模式的优点:
解释器模式的缺点:
举例:实现简单的加减法计算器,具体代码实现详情见:
设计模式之解释器模式(C++)_翟天保Steven的博客-CSDN博客

上述讲了这么多关于设计模式的内容,希望我举的例子能帮助你相对容易地理解设计模式。每个模式的链接点进去,都是专门的一篇文章,且附带了完整详细简单的源码,相信多少会给你带来一些帮助!
如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
我正在尝试创建密码规则来设计可恢复的密码更改。我通过passwords_controller.rb做了一个父类(superclass),但我需要在应用规则之前检查用户角色,但我所拥有的只是reset_password_token。 最佳答案 假设您的模型是用户:User.with_reset_password_token(your_token_here)Source 关于ruby-on-rails-设计通过reset_password_token获取用户,我们在StackOverflow
我已经使用Apartment设置了一个Rails5应用程序(1.2.0)和Devise(4.2.0)。由于某些DDNS问题,应用只能在app.myapp.com下访问(请注意子域app)。myapp.com重定向到app.myapp.com。我的用例是每个注册该应用的用户(租户)都应该通过他们的子域(例如tenant.myapp.com)访问他们的特定数据。用户不应限定在其子域内。基本上应该可以从任何子域登录。重定向到租户的正确子域由ApplicationController处理。根据Devise标准,登录页面位于app.myapp.com/users/sign_in。这就是问题开始的