《大话设计模式》学习笔记
[TOC]
本文为《大话设计模式》一书的学习笔记以及总结.
写在读完《大话设计模式》后
学习这本书的起因是工作/日常写代码过程中明显会时时刻刻感受到自己的代码需要重构,因为每次需要增加新功能或者是修改bug时都要修改很多,觉得当初的想法很不成熟导致了后期维护的困难.然而一开始想好了架构再写代码很多时候是来不及的,领导的需求,部门间的扯皮,对某个轮子的不熟悉等等都会导致架构根本定不下来.即便定下来了,也不是非常合理的,导致前期投入的性价比很低还不如直接上,先出效果,后面再重构.
这些实践必然是非常坏的,既不符合工程的严谨性更不fashion.我相信前人已经解决了一定程度上我遇到的这种痛点.更好的practice基于更具有指导意义的范式在哪里呢?于是我把目光投向了设计模式.
有些人会说设计模式本来就是自然而然的东西,工作/实践中碰壁多了自然会有自己的一套方式,然后会发现与专家总结出来的所谓的设计模式不谋而合.然而对我而言,我正是想要少碰壁或者是碰壁后更好更快地回头才会想到学习设计模式.同时轮子的主人们的设计思路里大概率也是有设计模式的影子的,越快掌握设计模式越快能与大牛们的思路接上轨,实现飞轮效应.因此我觉得学习的时间花的是值得的.
至于为什么会是这本书,我大概看了一下GoF的《设计模式 可复用面向对象软件的基础》太偏于学术化,不适合时间紧张的目前的我.《 Head First 设计模式 》里面太多花里胡少的东西.最终选择了《大话设计模式》,案例接地气,容易记忆,覆盖的也很全面,虽然干货密度不高.
书看完发现其实我已经在接触/使用了一些设计模式了.比如算法函数抽象出来的策略模式,通过std::unordered_map
生产标签为N的类,将类中最具代表性的数据保存出来方便随时再次生成类的原型+备忘录模式等.接触到的ROS中的parameter server与中介模式有异曲同工之妙,msg的收发是观察者模式,STL里面就更不用说了迭代器,算法,函数对象与迭代器模式,访问者模式,策略模式等是明显的对应.Baidu Apollo里的抽象工厂模式,单例模式等等.
这本书学完也只是开始,距离忘记所有招式化有招为无招还很远,需要继续实践,继续总结.
同时本书是基于C#的,不可否认的是语言对于设计模式的实现以及细节有很大不同,对于C++的探索也在进行中.
文中出现的UML类图可见学习博文.
第0章 总结
以下总结来自C语言中文网的总结.
七大设计原则
这些原则的目的只有一个:降低对象之间的耦合,增加程序的可复用性,可扩展性和可维护性.
设计原则 | 一句话归纳 | 目的 |
---|---|---|
开闭原则 | 对扩展开放,对修改关闭 | 降低维护带来的新风险 |
依赖倒置原则 | 高层不应该依赖低层,要面向接口编程 | 更利于代码结构的升级扩展 |
单一职责原则 | 一个类只干一件事,实现类要单一 | 便于理解,提高代码的可读性 |
接口隔离原则 | 一个接口只干一件事,接口要精简单一 | 功能解耦,高聚合,低耦合 |
迪米特法则 | 不该知道的不要知道,一个类应该保持对其它对象最少的了解,降低耦合度 | 只和朋友交流,不和陌生人说话,减少代码臃肿 |
里氏替换原则 | 不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义 | 防止继承泛滥 |
合成复用原则 | 尽量使用组合或者聚合关系实现代码复用,少使用继承 | 降低代码耦合 |
其中,抽象类与接口的区别:
- 类是对对象的抽象,抽象类是对类的抽象,接口是对行为的抽象.
- 如果行为跨越不同类的对象,可以使用接口;对于一些相似的类对象,用继承抽象类.
- 从设计的角度讲,抽象类是从子类中发现了公共的东西,泛化出父类,然后子类继承父类,而接口根本不知道子类的存在,方法如何实现还不确认,预先定义.
23种模式归纳表
分类 | 设计模式 | 简述 | 一句话归纳 | 目的 | 生活案例 |
创建型设计模式(简单来说就是用来创建对象的) | 工厂模式(Factory Pattern) | 不同条件下创建不同实例 | 产品标准化,生产更高效 | 封装创建细节 | 实体工厂 |
单例模式(Singleton Pattern) | 保证一个类仅有一个实例,并且提供一个全局访问点 | 世上只有一个我 | 保证独一无二 | CEO | |
原型模式(Prototype Pattern) | 通过拷贝原型创建新的对象 | 拔一根猴毛,吹出千万个 | 高效创建对象 | 克隆 | |
建造者模式(Builder Pattern) | 用来创建复杂的复合对象 | 高配中配和低配,想选哪配就哪配 | 开放个性配置步骤 | 选配 | |
结构型设计模式(关注类和对象的组合) | 代理模式(Proxy Pattern) | 为其他对象提供一种代理以控制对这个对象的访问 | 没有资源没时间,得找别人来帮忙 | 增强职责 | 媒婆 |
外观模式(Facade Pattern) | 对外提供一个统一的接口用来访问子系统 | 打开一扇门,通向全世界 | 统一访问入口 | 前台 | |
装饰器模式(Decorator Pattern) | 为对象添加新功能 | 他大舅他二舅都是他舅 | 灵活扩展,同宗同源 | 煎饼 | |
享元模式(Flyweight Pattern) | 使用对象池来减少重复对象的创建 | 优化资源配置,减少重复浪费 | 共享资源池 | 全国社保联网 | |
组合模式(Composite Pattern) | 将整体与局部(树形结构)进行递归组合,让客户端能够以一种的方式对其进行处理 | 人在一起叫团伙,心在一起叫团队 | 统一整体和个体 | 组织架构树 | |
适配器模式(Adapter Pattern) | 将原来不兼容的两个类融合在一起 | 万能充电器 | 兼容转换 | 电源适配 | |
桥接模式(Bridge Pattern) | 将两个能够独立变化的部分分离开来 | 约定优于配置 | 不允许用继承 | 桥 | |
行为型设计模式(关注对象之间的通信) | 模板模式(Template Pattern) | 定义一套流程模板,根据需要实现模板中的操作 | 流程全部标准化,需要微调请覆盖 | 逻辑复用 | 把大象装进冰箱 |
策略模式(Strategy Pattern) | 封装不同的算法,算法之间能互相替换 | 条条大道通罗马,具体哪条你来定 | 把选择权交给用户 | 选择支付方式 | |
责任链模式(Chain of Responsibility Pattern) | 拦截的类都实现统一接口,每个接收者都包含对下一个接收者的引用.将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止. | 各人自扫门前雪,莫管他们瓦上霜 | 解耦处理逻辑 | 踢皮球 | |
迭代器模式(Iterator Pattern) | 提供一种方法顺序访问一个聚合对象中的各个元素 | 流水线上坐一天,每个包裹扫一遍 | 统一对集合的访问方式 | 逐个检票进站 | |
命令模式(Command Pattern) | 将请求封装成命令,并记录下来,能够撤销与重做 | 运筹帷幄之中,决胜千里之外 | 解耦请求和处理 | 遥控器 | |
状态模式(State Pattern) | 根据不同的状态做出不同的行为 | 状态驱动行为,行为决定状态 | 绑定状态和行为 | 订单状态跟踪 | |
备忘录模式(Memento Pattern) | 保存对象的状态,在需要时进行恢复 | 失足不成千古恨,想重来时就重来 | 备份,后悔机制 | 草稿箱 | |
中介者模式(Mediator Pattern) | 将对象之间的通信关联关系封装到一个中介类中单独处理,从而使其耦合松散 | 联系方式我给你,怎么搞定我不管 | 统一管理网状资源 | 朋友圈 | |
解释器模式(Interpreter Pattern) | 给定一个语言,定义它的语法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子 | 我想说"方言",一切解释权都归我 | 实现特定语法解析 | 摩斯密码 | |
观察者模式(Observer Pattern) | 状态发生改变时通知观察者,一对多的关系 | 到点就通知我 | 解耦观察者与被观察者 | 闹钟 | |
访问者模式(Visitor Pattern) | 稳定数据结构,定义新的操作行为 | 横看成岭侧成峰,远近高低各不同 | 解耦数据结构和数据操作 | KPI考核 |
第1章 代码无错就是优?——简单工厂模式
简单工厂模式:
专门用来生产特定对象的模式.
下图为计算器运算符实现.
核心在于
- 实现与接口分离.
- 简单工厂作为接口只对接1个抽象实现类.
但是简单工厂模式不满足开闭原则,不算在23个设计模式内.
第2章 商场促销——策略模式
策略模式:定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式下的算法的变化不会影响到客户端的使用.
要点:
- 不同算法(策略)是独立可相互替换的.父类可以是共同操作的抽象.
- 可以与简单工厂模式结合,在Context的工厂里完成算法对象的实例化.
- 算法的具体选用由客户端决定并传达给Context工厂.
第3章 拍摄UFO——单一职责原则(SRP)
单一职责原则:就一个类而言,应当仅有一个引起它变化的原因.
一个类里职责过多会导致职责的耦合,一个职责的变化可能会抑制或者削弱其他职责的能力.
第4章 考研求职两不误——开放-封闭原则(OCP)
开放-封闭原则:软件实体(类,模块,函数等)可以拓展但不可被修改,open for extension, close for modification.
要点:
- 阶段性地提前预判变化可能,进行合适的抽象(封闭).
- 一旦遇到未预测到的变化,尽快重构抽象,隔离类似问题,同时加深思考有没有其他变化未考虑到(PDCA行动模式).
- 越早预测/发现问题影响越小.
- 抽象也要适度,不能过度抽象.
第5章 会修电脑不会修收音机?——依赖倒转原则
依赖倒转原则:高层模块不应该依赖低层模块,两者都应该依赖抽象(接口).抽象不应该依赖细节,细节应该依赖抽象.也就是要面向接口编程而不是面向实现编程.这个原则是面向对象设计的标志.
例如,如果高层的业务逻辑强依赖于低层的函数库(数据库/运算库),一旦客户要求使用别的数据库,高层低层均无法复用.应该让高层低层都去依赖接口.
里氏代换原则(LSP):子类必须能够替换它们的父类.
只有当子类可以替换掉父类同时保证功能不受影响时,父类才能被真正复用,子类才能在父类的基础上添加新的功能.
第6章 穿什么有这么重要?——装饰模式
装饰模式:动态给一个对象添加一些额外的职责,就增加功能而言,对比子类添加,装饰更加灵活.
同时也可以考虑在设计之初就把核心职责与装饰功能分开.所谓的装饰功能指的是仅仅满足在某种特定情况下才会执行特殊行为的功能.
也要注意多个装饰功能之间顺序的影响.
装饰功能的实现是通过Decorator类继承主抽象类(或者是实体类),重载一些函数/创建新函数实现的.
图中如果 ConcreteComponent 类只有一个类也可以不需要 Component 类,即把 ConcreteComponent 与 Component 类合并.同理对 ConcreteDecorator 与 Decorator.
第7章 为别人做嫁衣——代理模式
代理模式:为其他对象提供一种代理以控制对这个对象的访问.
要点:
- 实际与代理共同继承同一接口,保证两者实现的结果一致.
- 代理中保存一个引用以访问实际.
使用场景:
- 远程代理:隐藏一个对象存在于不同地址空间的事实.
- 虚拟代理:实际对象的创建需要很长时间,设置代理加速创建,例如HTML页面的加载.
- 安全代理:控制访问权限.
- 智能指引:使实际与代理可以并行处理一些事物.
第8章 雷锋依然在人间——工厂方法模式
工厂方法模式:一个用于创建对象的接口,让子类决定去实例化哪一个类.工厂方法使一个类的实例化延迟到其子类.
要点
- 对比简单工厂模式,工厂方法模式增加了对factory抽象类的实体化.
- 好处是符合了开放-封闭原则.
- 坏处1是原本在工厂抽象类进行的制造选择判断逻辑转移到了客户端.
- 坏处2是增加了开发代码量(实例出多个工厂实体类).
第9章 简历复印——原型模式
原型模式(Prototype):用原型势力指定创建对象的种类,并且通过拷贝这些原型创建新的对象.
要点:
- 不用重新初始化对象,而是动态地获取对象运行时的状态.
- 也不需要知道任何创建的细节.
- 但是要注意深copy与浅copy,一般类的复制除了静态字段采用完全复制,但是对于引用的复制只复制引用而不复制引用的内容(浅copy),如果需要copy引用的内容需要注意层次与循环引用的风险.
第10章 考题抄错会做也白搭——模板方法模式
模板方法模式:定义一个算法中的操作骨架,而将一些步骤延迟到子类中.模板方法可以使得子类不改变一个算法结构即可重新定义算法的某些特定步骤.
要点
- 把不变的行为搬到超类去除重复代码.
- 需要变的行为由子类单独实现,此时超类中的类根据需要设置成virtual.
第11章 无熟人难办事?——迪米特法则
迪米特法则(LoD):也叫最少知识原则,如果两个类不必直接通信,那么两个类不应该发生直接的相互作用.如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用.
也就是强调弱耦合.
第12章 牛市股票还会亏钱?——外观模式
外观模式:为子系统的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加易用.
要点
- 这个模式更适合更大层面的应用.例如系统与系统之间.Facade相当于子系统的容器.
使用场景
- 设计初期阶段,有意识地将不同层次分离,层与层之间建立Facade,例如MVC架构之间.
- 开发阶段,子系统发展越来越多,越来越复杂,需要建立Facade降低耦合.
- 维护历史遗留大项目时,虽然难以维护与拓展,但是还是有些功能需要使用,可以创建一个Facade,介于旧项目与新系统之间.
第13章 好菜每回味不同——建造者模式
建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.
要点
- 适合创建较复杂的对象,对象内部建造的大步骤固定,每个步骤里又可以进一步指定.
- 固定的大步骤构建为抽象Builder类,Director为指挥者用于与外部接口,决定构建什么样的Product.
同为创建对象的模式,工厂模式与建造者模式的区别(参考链接):
意图不同
在工厂方法模式里,我们关注的是一个产品整体,无须关心产品的各部分是如何创建出来的;但在建造者模式中,一个具体产品的产生是依赖各个部件的产生以及装配顺序,它关注的是”由零件一步一步地组装出产品对象”.简单地说,工厂模式是一个对象创建的粗线条应用,建造者模式则是通过细线条勾勒出一个复杂对象,关注的是产品组成部分的创建过程.产品的复杂度不同
工厂方法模式创建的产品一般都是单一性质产品,都是一个模样,而建造者模式创建的则是一个复合产品,它由各个部件复合而成,部件不同产品对象当然不同.这不是说工厂方法模式创建的对象简单,而是指它们的粒度大小不同.一般来说,工厂方法模式的对象粒度比较粗,建造者模式的产品对象粒度比较细.
第14章 老板回来,我不知道——观察者模式
观察者模式:又被叫在发布-订阅(Publish-Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者同时监听某一主题对象(Object).这个主题对象在状态发生变化时会通知所有的观察者对象,使观察者能更新自己的状态.
这个模式就是ROS的消息收发的模型.
- Observer就是ROS里的节点Node模型接收特定Topic后进行一定的运算,具体的ConcreteObserver是用户自己创建的Node,例如专门实现在Rviz上进行可视化的功能.
- Subject就是Topic的发出者.只不过ROS中的发出者不需要管理接受者的名单(图中Attach,Detach操作).
为了实现ROS一样的非管理观察者名单的功能,可以采用事件委托,如果用C++里的概念去描述则是函数对象,对应到ROS中,为node.publish()的函数模板.核心思想是每个Observer需要更新状态的函数/动作传给Subject(Publisher),这样Subject就不用再去管理Observer,实现解偶.
第15章 就不能不换DB吗?——抽象工厂模式
抽象工厂模式(Abstract Factory):提供一个创建一系列相关或相互依赖的接口,而无需指定它们具体的类.
要点
- 在工厂模式的基础上增加了对Product的抽象.
- 好处是方便制造同类的Product.
- 坏处是每增加一种Product类,需要增加两种类(Product抽象类,实体类),修改对应的工厂类,代码量变大.
使用反射+抽象工厂省去工厂实体化时所用的switch case语句(因为运行时才能确定到底实体化哪个工厂).
由于C++中不存在反射机制,因此需要手动实现这一思路:散列表+模板函数.具体可以参考文章.
第16章 无尽加班何时休——状态模式
状态模式(State):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类.
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化,消除庞大的条件分支语句.
第17章 在NBA我需要翻译——适配器模式
适配器模式(Adapter):将一个类的接口转换成客户希望的另一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.
系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配.适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况.在双方都不太容易修改的时候再使用适配器模式适配,应该优先考虑重构.
适配器模式有两种类型,类适配器模式(支持多重继承的才有)和对象适配器模式.
C++ STL中适配器的概念就很常见了,
- 容器适配器:stack,queue,priority_queue.
- 函数对象适配器:binder.
- 迭代器适配器:reserve_iterator(),back_inserter(),inserter(),front_inserter(),istream_iterator(),ostream_iterator().
第18章 如果再回到从前——备忘录模式
备忘录:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态.
Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态.Originator可根据需要决定Memento存储Originator的哪些内部状态.
Memento(备忘录):负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento.备忘录有两个接口,Caretaker只能看到备忘录的窄接口,它只能将备忘录传递给其他对象.Originator能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据.
Caretaker(管理者):负责保存好备忘录Memento,不能对备忘录的内容进行操作或检查.
用处
Memento模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态.
第19章 分公司=一部门——组合模式
组合模式(Composite):将对象组合成树形结构以表示部分-整体的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性.
Component为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为.声明一个接口用于访问和管理Component的子部件.Component可以视为组合后的整体对象.
Leaf在组合中表示叶节点对象,叶节点没有子节点.
Composite定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关的操作,比如增加Add和删除Remove,同时与Leaf共享父类Component的操作/方法.
用处
需求中是体现部分与整体层次的结构时 ,以及希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时.
例如下面公司与多部门的层级关系.
第20章 想走?可以!先买票——迭代器模式
迭代器模式:提供一种方法顺序访问一个聚合对象的各个元素,而又不暴露该对象的内部表示.
没啥可讲的,STL里的迭代器要比书里的复杂太多.
第21章 有些类也需计划生育——单例模式
单例模式(Singleton):保证一个类仅有一个实例,并提供访问它的全局访问点.
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象.一个最好的办法就是,让类自身负责保存它的唯一实例.这个类可以保证没有其他实例可以被创建(构造函数为private,由一个public static函数创建唯一一个),并且它可以提供一个访问该实例的方法.
用处
实现对唯一实例的受控访问.
多线程时注意多个线程可能会创建出多个实例出来,保证创建一个并使用lock.
Double-Check Locking(双重锁定):我们不用让线程每次都加锁,而只是在实例未被创建的时候再加锁处理.同时也能保证多线程的安全.
呃,这个做法不就是《C++ Concurrency in Action》里批判的做法吗,因为第一次判断为不为空也会造成race condition.
饿汉式单例类:静态初始化的方式是在自己被加载时就将自己实例化.
懒汉式单例类:要在第一次被引用时,才会将自己实例化.
第22章 手机软件何时统一——桥接模式
合成/聚合复用原则(CARP):尽量使用合成/聚合,尽量不要使用继承.
合成(Composition,也有翻译成组合)和聚合(Aggregation)都是关联的特殊种类.聚合表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样.
下面两张图是基于继承以及聚合的对比.
桥接模式(Bridge):将抽象部分与它的实现部分分离,使他们都可以独立地变化.
第23章 烤羊肉串引来的思考——命令模式
命令模式(Command):将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作.
Command类,用来声明执行操作的接口.
1 | abstract class Command |
ConcreteCommand类,将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现Execute.
1 | class ConcreteCommand : Command |
Invoker类,要求该命令执行这个请求.
1 | class Invoker |
Receiver类,知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者.
1 | class Receiver |
客户端代码,创建一个具体命令对象并设定它的接收者.
1 | static void Main(string[] args) |
命令模式的优点:
- 它能较容易地设计一个命令队列;
- 在需要的情况下,可以较容易地将命令记入日志;
- 允许接收请求的一方决定是否要否决请求 .
- 可以容易地实现对请求的撤销和重做;
- 由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易.
- 把请求一个操作的对象与知道怎么执行一个操作的对象分割开.
敏捷开发原则告诉我们,不要为代码添加基于猜测的,实际不需要的功能.如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义.
第24章 加薪非要老总批?——职责链模式
职责链模式(Chain of Resposibility):使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求直到有一个对象处理它为止.
Handler:对客户端的接口.
ConcereteHandler:具体处理的类,如果能处理请求就处理,不能处理转发给下一个 ConcereteHandler 传递.
要点
- 好处是降低耦合,每个环节都是单进单出,解决分支多混乱的情况.
- 坏处是需要考虑都处理不了情况.
- 应对跨级处理时,可以通过set下一级类来实现.
第25章 世界需要和平——中介者模式
中介者模式(Mediator):用一个中介对象来封装一系列的对象交互.中介者使得各对象不需要显式地相互引用,从而实现耦合松散,而且可以独立地改变它们之间的交互.
例子如下:
要点:
- 降低 Colleague 之间的耦合,方便 Colleague 的复用.
- Colleague 间的交互被抽象成独立概念,可以集中式管理(例如,通信的带宽分配等).
- 代码复杂度从 Colleague 间转移到了 Mediator 上,Mediator可能会维护困难.
- 该模式适合于一组对象定义良好但是以复杂方式进行通信的场合.
这个模式让我想起了ROS中的Parameter server的概念,应该就是这个模式思想的体现.
第26章 项目多也别傻做——享元模式
享元模式(Flyweight):运用共享技术有效地支持大量细粒度的对象.
要点
- 与三种工厂模式结合使用.
- 可以避免大量相似类的开销,所谓的相似类是指除了少数几个参数不同以外,大多数数据都是一样的,如果使用享元模式的话能大幅度减少实例化类的数量.
- 享元模式会增加管理共享元素的开销,以及为了实现共享需要把一些状态外部化,只有元素实例数目多到一定程度才值得使用享元模式.
- 有必要的话也可以设置UnsharedConcreteFlyweight适用于非共享的实例.
实际应用:std::string
的引用计数,两个相同的string,刚开始都指向一个内存,如果其中一个改变了才会分配2份不同的内存分别使用.
第27章 其实你不懂老板的心——解释器模式
解释器模式(Interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子.
第28章 男人和女人——访问者模式
访问者模式(Visitor):表示作用于某对象结构中的各元素的操作.它可以使不改变各元素的类的前提下定义作用于这些元素的新操作.
要点
- Element类不会发生什么变化,否则Visitor下面的操作类可能都得改.
这个模式让我想起了STL里的算法对容器的操作.容器提供了结构不变的数据,用户可以选择不同的算法实现期望的结果.
第29章 OOTV杯超级模式大赛——模式总结
见第0章.