大话设计模式随记

GoF:


必背

24种设计模式 :

  • 设计原则: 设计模式(总纲)
  • 创建型(6): 单例模式 简单工厂模式 工厂方法模式 抽象工厂模式 建造者模式 原型模式
  • 结构型(7 ,2个器,5个两字的): 代理模式 适配器模式 装饰器模式 桥接模式 组合模式 享元模式 外观模式
  • 行为型(11): 观察者模式 模板方法模式 命令模式 状态模式 职责链模式 解释器模式 中介者模式
  • 访问者模式 策略模式 备忘录模式 迭代器模式

3个者 ,3个两字的,2个器,2个三字的,1个四字的

六大规则 :

  • 单一职责原则 (Single Responsiblity Principle SRP)
  • 开闭原则(Open Closed Principle,OCP)
  • 里氏代换原则(Liskov Substitution Principle,LSP)
  • 依赖倒置原则(Dependency Inversion Principle,DIP)
  • 接口隔离原则(Interface Segregation Principle,ISP)
  • 最小知识原则(Principle of Least Knowledge,PLK,也叫迪米特法则)

其他介绍

  • 单一职责原则:描述的意思是每个类都只负责单一的功能,切不可太多,并且一个类应当尽量的把一个功能做到极致。
  • 里氏替换原则:这个原则表达的意思是一个子类应该可以替换掉父类并且可以正常工作。(由于有了里氏替换原则,才使得开发-关闭有了可能,因为子类的可替换性使得使用父类类型的模块在无需修改的情况下就可以扩展)
  • 接口隔离原则:也称接口最小化原则,强调的是一个接口拥有的行为应该尽可能的小。
  • 依赖倒置原则:这个原则描述的是高层模块不该依赖于低层模块(高层要通过抽象或者接口依赖底层的意思,如Controller是高层,Dao是低层),二者都应该依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象。 即针对接口编程,不要针对实现编程。(接口的好处是只要把对外接口定义好,那么内部实现无论多复杂,怎么改动都和外部调用者无关)
  • 迪米特原则:也称最小知道原则,即一个类应该尽量不要知道其他类太多的东西,不要和陌生的类有太多接触。
  • 开-闭原则:最后一个原则,一句话,对修改关闭,对扩展开放。

其他随笔:

如果想成为一名更优秀的软件设计师,了解软件设计的演变过程比学习优秀设计本身更有价值,因为设计的演变过程中蕴含着大智慧。

理解:是不是说其实目前我们所用到的优秀中间件,框架,MQ,Zookeeper,redis等想学习了深入理解他们首选就要先知道为什么我们选择他,他解决了哪些痛点,难点?又是如何一步一步演化为先如今的功能样式的。在设计模式中,除了了解这个设计模式是什么?还要了解为什么这样设计才是好的?如何想到这样的设计的?

WHAT WHY HOW


P16 :UML图的认识

面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。

所以因为一点变化就创建一个类是不行的,得是一种对象的描述才行。如动物下分鸡鸭鱼,而不是一件商品打678折或者满500减100这种情况划分类。

算法或者细节等如打折的改变一般喜欢用策略模式来封装。策略模式就是用来封装算法的,在实践中,我们可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

尽量的让客户端或者调用者认识(或者说引用,创建都可以)最少的类或者说知识,其他调用端不必认识或者知道的全部封装起来,遵守最小知道原则。

高手和菜鸟的区别就是高手可以花同样的代价获取最大的利益或者说做同样的事花最小的代价。

单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。(因为他职责单一,所以只有份属于他的职责功能变化了才去改变他,其他情况下他是不变的。)

模板方法模式

模板方法模式:定义一个操作中的算法的骨架(流程),而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

大体是抽象类定义算法的股价,具体子类重定义相关细节的方法来实现具体的步骤。模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现他的优势。是一个 很好的代码服用平台,可以帮子类摆脱重复的不变行为的纠缠。

例子:每个考生的考试试卷及答案可以提炼为模板方法模式,即除了答案作为抽象方法给每个考生重写实现,其他不变的框架都在父类上定义。

大众汽车的生产,流程都是一样的,但是具体的细节如用的材料,车型,定位的不同而生产出不一样的大众汽车,高尔夫,途观,帕萨特。

汽车玻璃的生产,不同的汽车有不同的挡风玻璃形状,但生产挡风玻璃的流程和模型是一样的,只是根据不同的汽车玻璃形状来生产。

模板方法中,所以不变的都提升到父类中,再让具体的变化用子类去重写实现。

当我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别不走在更详细的层次上的实现可能不同时,通常选择用模板方法模式来处理。

迪米特法则

也叫最小知道原则

没有管理,单靠人际关系协调是很难办成事儿的。因为三个和尚没水喝,互相推诿。

定义:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另外一个类的某个方法的话,可以通过第三者转发这个调用。???第三者?(意思是我想要丈母娘做晚饭我不需要直接叫她,我可以通过宝宝来转发调用)

强调了类之间的松耦合,耦合越弱,越有利于复用,这样当一个弱耦合的类被修改,不会对有关系的类造成波及。

外观模式

体现依赖倒置和迪米特原则

定义:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

即让调用者调用一个统一的入口接口,这个接口整合了需要被调用类的功能,通过引用整合,向外提供方法服务。

例子:三层架构Controller》Service》Dao ,层与层之间建立外观Facade,即都向上提供服务;浙江ocrm老系统属于遗留大型系统,与其交互运用外观模式,通过设计一个Facade类,来提供清晰简单的接口,让订单中心与Facade对象交互,Facade对象与ocrm老系统交互复杂的工作;不是直接买股票而是通过基金来买股票,基金就是外观对象;功能按钮,一个功能里面有多个功能组成。

俄罗斯方块游戏的逻辑本质:

游戏区域可以设计为一个二维整形数据来表示坐标,如宽10,高20.例子:

int [][] arraySquare = new int[10][20];

那么整个方块的移动其实就是数组的下标变化,而每个数组的值就是是否存在方块的标志,存在为1,不存在缺省为0.

单一职责原则:

职责分离是因为如果一个类承担的职责过多,就等于把这些类耦合在一起,需要分离开,如找出哪些是界面,哪些是游戏逻辑,进行分离。

开闭原则:

典型例子:一国两制.

上班时间打卡制度:对工作时间或业绩成效的修改关闭,对时间制度拓展的开发,如弹性工作、打卡时间波动8:30-9:30

软件实体(类、模块、函数等)应该可以扩展,但不能修改。但这只是理想上的原则,要尽量做到,则必须在设计时先猜测出最有可能发生的变化种类,然后构造抽象来隔离这些变化。

面对需求,对程序的改动是增加新代码进行的,而不是更改现有的代码。而这也就需要通过构造抽象来隔离,

如计算器构造运算类,加减乘除继承该运算类;导出excel文件的导出基类,具体的导出文件继承基类来丰富导出字段等。

面向对象的4大好处:可维护,可扩展,可复用,灵活性好。

PC电脑硬件的发展,和面向对象思想的发展是类似的。(遵循6大准则)

程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之就是过程化的设计了。

时间万物都是遵循某种类似的规律,谁先把握了这些规律,谁就最早成为了强者。

装饰器模式

例子:人和衣服搭配;I/O流的装饰器类;工厂的产品包装,如糖果和外包装,汽车和内饰配置。

建造者模式要求建造的过程必须是稳定的,与装饰器模式的区别是装饰器模式的建造过程是不稳定的,他可以有非常多种搭配。

装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活(装饰模式也是要生成继承于组件的子类,然后在该子类基础上增加set该组件的功能,满足后面继承该装饰器类的子类可以在不影响组件功能的情况下增加额外功能。)

装饰模式的使用特点:

装饰模式是为已有功能动态地添加更多功能的一种方式。避免在已有的主类上加入新的字段,方法等增加了主类的复杂度,且新加的额外功能只适用于满足一些特定情况下才会执行的特殊行为的需要。这时候如果用装饰模式,把要装饰的功能放在单独的类中,让这个类包装他要装饰的对象,则当需要执行特殊行为或者说额外功能时,就可以有选择按顺序地运行使用装饰功能的包装对象了。(优点:有效地把类的核心职责和装饰功能区分开)

代理模式

中间商

例子:签证代理,开公司代理

代理模式中真实实体和代理类共用接口,即实现同一个接口,这其中代理类会保存一个真实实体的引用,在调用方调用接口方法时,代理类就能通过多态代替实体。

使用场合:

远程代理 :?

虚拟代理: html网页上的图片框通过虚拟代理代替了真实的图片,存储了真是图片的路径和尺寸,这样等加载好了就能替换过来,在这之前通过虚拟代理保持了网页的格式和轮廓。

安全代理:用来控制真实对象访问时的权限。

智能指引:指调用真实的对象时,代理处理另外一些事,也就是除了代理的事还多做些其他事。

工厂方法模式

gg

例子:大规模生产时,如富士康不同款的手机生产对应不同的流水线。

工厂方法模式是在简单工厂模式的基础上的抽象和扩张,即在简单工厂模式上,把工厂抽象出来,让要生产的具体产品分别有对应生产该产品具体的工厂类(继承于抽象工厂,通过多态来实现),其优点在于把简单工厂的内部逻辑判断(选择生产什么产品)转移到了客户端代码上来。现在想要增加新的产品,本来是修改工程类,现在是修改客户端调用,增加新的工厂,克服了简单工厂违背的开发-封闭原则的缺点。

(PS:但还是没能避免修改客户端的代码,可以通过简单工厂模式加反射来避免工厂类中的分支判断问题替代工厂方法。)

原型模式

复制

例子:打印机;简历复制;传单;电影胶卷

原型模式:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

提供一个clone方法来进行原型对象复制。

clone有深复制和浅复制区分。

一般在初始化信息不发生变化的情况下,克隆是最后的办法。这既隐藏了对象创建的细节,又大大提高了性能(为什么这么说?)。

建造者模式

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

建筑这模式的过程是稳定的,而具体建造的细节是不同的,使用建造者模式时,用户只需指定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需要知道了。

构建一个抽象的建造类,让要实现的产品继承这个建造类,实现里面的抽象方法(建造细节)。

还要有个指挥者类(项目经理),用它来控制建造过程,也用它来隔离用户与建造过程的关联;即用户把想要的产品建造类给我,具体怎么组装用户想要的产品交给指挥者。

例子:建造人;肯德基麦当劳的炸鸡生产是建造者模式,而不是靠厨师;建造房子。

建造者模式是在当创建复杂对象的算法(方法)应该独立于该对象的组成部分以及他们的装配方式时适用的模式。

那建造者模式和模板方法的区别?

A:模板方法模式是纯粹给算法用的,用于算法架构与算法细节分离;而建造者模式是给算法和他的组成部分用的,用于算法和对象结合时用?

观察者模式

观察者模式,又叫发布/订阅模式

一对多关系用

一般是有个接口类主题或者叫抽象通知者(Subject),一个接口类观察者(Observer)。具体的主题如微博关注的明星实现接口类主题,粉丝实现观察者。

依赖倒置原则的最佳体现。

例子:idea启动后启动按钮的变化,log窗口的显示等就是依赖于观察者模式;订阅报纸;微博关注的人更新微博,粉丝就能收到。

定义:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这些主题对象在状态发送变化时,就会通知所有的观察者对象,使他们能够自动更新自己,做自己受到变化通知后想调整的逻辑状态。

当我们将一个系统分割成一系列互相协作的类时会因为需要维护相关对象间的一致性(因一个主题变化而变化)而使得各类间紧密耦合,这样会给维护、扩展、重用都带来不便。这时候当一个对象的改变需要同时改变其他对象且不清楚具体有多少对象需要改变时,最佳方法便是观察者模式来,解除耦合。

而当需要变化的观察者已经被封装为一个个控件,无法实现观察者接口时,我们就需要通过事件委托(java中是监听器)来实现观察者模式。

抽象工厂模式

工厂方法模式适合少数几个产品系列问题,当要解决涉及多个产品系列问题是,要用抽象工厂模式。

例子:数据库访问(有mysql,Oracle等不同的数据库类型)

提供一个创建一系列(该抽象工厂里面提供了创建一系列的产品的抽象方法,注意是一系列产品)相关或相互依赖对象的接口,而无需指定他们具体的类。抽象工厂和工厂方法的区别便是抽象工厂创建的产品是涉及多个的,而工厂方法不是。

可以用简单工厂模式替换抽象工厂模式,就是在简单工厂里加判断该实现哪一类对象switch...case模式。

而且我们可以在使用简单工厂模式的地方考虑用反射技术来去除switch 或if ,解除分支判断带来的耦合,因为反射时可以用变量来决定要运行时实例化的对象,这样就可以用传入的变量来决定要实例化的分支。(注意:要学习下怎么在简单工厂上加反射)

勉励

无痴迷,不成功 。一个程序员如果从来没有熬夜写程序的经历,就不能算是一个好程序员,因为他没有痴迷过,所以他不会有大成就。确实,成就和投入的经历和时间成正比。

状态模式

遵循单一职责原则、开发关闭原则

面向对象设计其实就是希望做到代码的责任分解。

使用时有个类包含了状态抽象类引用(工作类),各种不同的子类状态分别对应不同的行为处理(不同工作状态)

例子:上班状态,不同的状态不同的行为效率。军队上不同号角表示的不同战备警备状态,对于不同的处理方式。

定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

主要解决的是当控制一个对象状态转换的条件表达式过于复杂时,就把状态的判断逻辑转移到表示不同状态的一系列类当中,这样把复杂的判断逻辑简化(到各个类中)。

这样可以将特定状态相关的行为局部化,并吧不同状态的行为分割开来,消除了庞大的条件分支语句。

当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变他的行为时,使用状态模式。

适配器模式

例子:电源适配器(改变电压使得符合国内220V标准);翻译;可调节高度座椅。

在我们不能改变调用对接接口的双方的前提下,我们能做的就是想办法找个适配器。一般如两个公司系统间的数据和行为都正确,但接口不符时的对接。

定义:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

简单说就是需要的东西就在眼前,但却不能使用,短时间无法改造它,于是想办法适配是最好的选择。

类适配器模式(需要通过多重继承来对两个接口进行匹配,不适合java等单继承语言)和对象适配器模式(主要是这个)。

适配器模式中,通过用适配类继承目标类,即客户端想调用的类来实现适配。适配类内部包装了被适配的私有对象,在调用方法中实际是通过私有对象调用想要的方法。适用于双方都不太容易修改的时候再使用适配器模式适配。

适配器模式主要用于软件维护中因功能类似而接口不同时所用的无奈之举,亡羊补牢,有点像扁鹊说自己的医术。如果是在设计阶段,应该考虑的不是适配,而是通过重构统一接口。

备忘录模式

例子:游戏进度保存;复制粘贴,网页前进后退这种频繁而简单的恢复保存在内存中;会议纪要。

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态。

备忘录模式中,需要保存状态的对象我们叫发起者,我们会定义一个备忘录类,发起者类上会定义一个保存状态到备忘录类的方法及相应提前状态方法。备忘录类负责存储发起者的状态(不提供get/set),把要保持的细节都封装在备忘录类上。再有一个便是备忘录类管理者(提供备忘录类对象的get/set方法)。负责保存备忘录类和提高备忘录类给发起者。

备忘录模式适合功能比较复杂,需要维护和记录属性历史的类,用于还原备份的信息到前一状态。还有在使用命令模式时,需要实现命令的撤销功能时,可以使用备忘录模式存储可撤销操作的状态。

缺点:如果要备份的状态数据很大很多时,备忘录对象会非常消耗内存资源。

组合模式

定义:将对象组合成树形结构以表示 部分-整体 的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

和HeadFirst设计模式(中文版)中说的组合模式描述不太相同,待确认。我的理解一般一个组件或者类中集合了其他功能组件最后就组合成了一个功能,这就是一种组合模式,如各个零部件组合成了手机。

当需求中体现部分和整体层次的结构时,希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所以对象时,考虑使用组合模式。

使用组合模式,用户不要关心到底是处理一个叶节点还是处理一个组合组件,也就用不着为定义组合而写一些选择判断语句了。即组合模式让客户可以一致地使用组合结构和单个对象。

待对比!!!

迭代器模式

例子:售票员售票,不管是人是物,只要上次就要买票。java里的迭代器遍历各种数据结构.foreach;iterator

定义:提供一种方法顺序访问一个聚合对象(数据结构集合)中各个元素,而又不暴露该对象的内部表示。(什么叫内部表示?)

当我们需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,应该考虑用迭代器模式。

迭代器模式即一个聚集抽象类,一个跌倒抽象类。在具体继承的聚集类里有个创建迭代器的方法,而在具体的迭代器里有对应聚集类的具体迭代器类,并定义了一个具体聚集对象的属性,用于在重写迭代器方法时用具体对象来完成相应的方法。

迭代器模式分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可以让外部代码透明地访问集合内部的数据。

单例模式

例子:只要一个数据库连接池;只要一个赖荣锋;只要一个创造工厂;

单例模式是否实例化应该由单例对象自身判断,通过构造方法私有来实现。不过现在java单例模式的写法一般是通过延迟初始化占位类模式来实现。

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

防止一个类实例化多个对象的最好办法是让类自身负责保存它的唯一实例。

一般单例类提供私有化的构造函数,并提供公有化静态方法来获取对象,去创建或访问单例类的唯一实例。

多线程时用双重检查加锁或者延迟初始化占位类模式来实现。一般用延迟初始化占位类模式比较好。

桥接模式

利用的原则是:合成(组合)/聚合复用原则。即优先使用对象合成/聚合,而不是类继承。大雁和它的翅膀是合成或者说叫组合关系,而大雁和雁群是聚合关系。两者之间的区别是强拥有和弱拥有的关系,组合体现了严格的部分和整体的关系,部分和整体的生命周期一样。而聚合是指雁群可以保护大雁,但大雁并不是雁群的一部分,可以很轻松地脱离关系。

优先使用对象的合成聚合有助于保持每个类被封装,并被集中在单个任务上,使得类与类继承层次保持较小规模。

面向接口编程,通过接口组装出想要的功能,继承太笨重。

定义:将抽象部分与他的实现部分分离,使它们都可以独立地变化。

两个抽象类之间有一条聚合线,像一座桥,所以叫桥接模式。

桥接模式是由于一个对象或者说功能的实现方式有很多种,如手机可以按品牌来分实现,也可以按照功能来分,而桥接模式的核心意图就是把这些实现独立出来,让他们各自地变化,这样就使得实现的变化不会影响其他实现,达到开发关闭的拓展原则,得以应对后续的变化。

如手机,有功能抽象类,下面有通讯录,照相机等功能实现,还有品牌抽象类,下面有苹果,小米等实现,在如苹果的实现类中其有设置功能抽象类进来的入参,使得苹果可以聚合通讯录,照相机等功能,而后面不管添加新的品牌或者功能,都只需要在各种中扩展即可,不需要重复。

当实现系统可能有多角度分类,每一种分类都有可能变化,那么就可以把这种多角度分离出来让他们独立变化,减少他们之间的耦合。也就是桥接模式。

例子:手机的功能和品牌区分。汽车的品牌和使用的驱动燃料不同的区分。

命令模式

例子:餐厅点餐;军队指挥,司令员通过通讯兵传达信息命令。

适用场景:当行为请求者和行为实现者紧耦合,且对请求排队或记录请求日志,以及支持可撤销的操作等行为时。

定义:将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化(什么意思?);对请求排队或记录请求日志,以及支持可撤销的操作。

一般是客户端通过一个专门的调用者如服务员(包含一个命令集List)发出命令,命令类接口底下实现各种命令类(包含一个接受者对象),而每个具体的命令类对应有接收命令的执行者,接受命令的执行者类做出真正的动作。

优点:

  1. 能较容易地设计一个命令队列
  2. 在需要的情况下,可以较容易地将命令记入日志
  3. 允许接收请求的一方决定是否要否决请求
  4. 可以容易地实现对请求的撤销和重做
  5. 可以不影响其他类加入新的具体命令类
  6. 解耦,命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。

其他:敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。

职责链模式

定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。

定义一个职责类handler接口,具体的处理类实现该接口,处理它负责的请求,每个具体的处理类都会有个后继者属性set进来,如果是可以处理的请求则处理掉,如不不是则将该请求转发给set进来的后继者去处理。后继者就是另一个具体的处理类,这样一直请求下去。

好处:当客户提交一个请求时,请求是沿着请求链传递直至有一个具体的处理类对象福州处理它。接受者和发送者都没有对方的明确信息,简化了职责链可简化对象的相互连接,因为它只需要保持一个指向其后继者的引用,而不需要保持所以的候选接受者的引用,极大降低了耦合度。

中介者模式

又叫调停者模式

例子:房产中介;签证中介;二手车中介;联合国;计算器中通过form窗体作为中介者来完成控件的交互。

前提:大量的连接使得一个对象不可能在没有其他对象的支持下工作,系统表现为一个不可分割的整体,而这样对系统的行为进行任何较大的改动就比较困难,因此有个同一个连接中介可以大大减少大量的连接,通过连接这个中介对象便能获得想要的所有连接(如国与国之间通过联合国发生关系),岂不美哉.哈哈哈哈哈哈哈哈哈哈(迪米特原则)

通过中介者对象,可以将系统的网状结构变成以中介者为中心的星形结构。

定义:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

一般定义一个中介者抽象类和一个同事抽象类,具体中介者基础该中介者抽象类,里面包含所有的同事属性,通过set方法set进来,并实现抽象类中用来交互的抽象消息发送方法;而具体同事类则实现同事抽象类,通过构造方法参数在创建同事类时把中介者对象添加进来,并提供发送和接收消息的方法。

在具体实践中,中介者类是否要抽象或者接口取决于未来是否需要扩展中介者对象,比如联合国除了安理会还有世界卫生组织,教科文组织等。

当系统出现了 “多对多” 交互复杂的对象群时,不要急于使用中介者模式,而要先反思你的系统在设计上是不是合理。

优点:减少了各个同事类的耦合,使得可以独立的修改和复用各个同事类和中介者类;使得我们可以站在更宏观的角度去看系统,由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各种本身的行为转移到天明直接的交互上来。从无数的双边关系转移到联合国关系上来。

缺点:由于中介者类控制集中化,于是就把交互复杂性变成了中介者的复杂性,使得中介者复杂度直线升高。

优点来着集中控制,缺点也来着集中控制,所以使用该模式时要考虑清楚。

一般应用于一组对象已定义良好但是交互通信复杂的场合,或者是想定制一个分布在多个类中的行为,而又不行生成太多的子类的场合(其实就是通过组合引用该行为功能吧?)。

享元模式

例子:博客网站;微博;邮箱;电商;字符串String;围棋、五子棋、跳棋(大量的棋子对象,使用享元最合适不过,如围棋创建两个黑白棋子的享元对象,外部状态为方位坐标)

定义:运用共享技术有效地支持大量细粒度的对象。

使用时会创建一个享元工厂,享元工厂里有个保存享元对象的hashtable集,让用户通过key/value形式获取享元对象,用来创建享元对象(Flyweight),一个享元抽象类,具体有实际的享元类,和不分享的实例享元类,客户端通过调用享元工厂获得享元对象,享元工厂在创建享元对象时通过参数传入外部状态。

内外部状态:在享元对象内部并且不会随环境改变而改变的共享部分,为享元对象的内部状态;

随环境改变而改变、不可以共享的状态就是外部状态了。

享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,使用享元模式便能够大幅度地减少需要实例化的类的数量。如果能把不同的地方一道类实例外面,在方法调用时将它们传递进来,就可以用通过共享大幅度地减少单个实施例的数目。

适用场景:如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。直观体现便是使用享元模式,实例总数大大减少了。

解释器模式

例子:不同浏览器解释HTML语言展示页面;正则表达式;乐谱演奏解释器;java虚拟机解释器;

定义:给定一个语言,定义他的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子,这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

一般是一个context类存放要解释的内容,一个抽象表达式类,包括解释和执行两个方法,通过简单功能加反射来决定具体选择哪种具体表达式类解释执行。

用解释器模式,相当于开发了一个编程语言或脚本给自己或别人用。解释器模式就是用 ‘迷你语言’ 来表现程序要解决的问题,以迷你语言写成 ‘迷你程序’ 来表现具体的问题。

当有一个语言需要解释执行,并且你可以将该语言中的句子表示为一个抽象语法树时(?),可使用解释器模式。

只要是可以用语言来描述的,都可以应用解释器模式。

优点:容易改变和扩展文法,因为该模式使用类来表示文法规则,我们可以使用继承来改变或扩展该文法;也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。

缺点:因为解释器为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。

所以当文法非常复杂时,使用语法分析程序或编译器生成器来代替处理。

访问者模式

定义:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

访问者模式使用时必须确定element元素是确定的,这样才能在状态(visitor)类上定义确定个数element的方法。一个抽象的element类,他有一个接受状态(visitor)参数的方法,底下具体实现如男人、女儿这样确定的具体元素。还有一个抽象的状态visitor类,他有对应不同元素的抽象方法,如男人的反应、女人的反应两个方法。具体的状态实现该抽象分别写出不同状态下男女不同的反应。另外有个对象结构(ObjectStructure),用于不同元素对应的逻辑展示,如针对不同的 ’状态‘ 遍历 男人 和 女人 ,得到不同的反应。客户端通过对象结构,添加元素如男人、女人。然后创建不同的状态,作为参数传递给对象结构展示方法,展示方法把状态作为参数传递给男人、女人,让男人女人通过接受这个方法加状态参数来调用各自男女不同的方法。

访问者模式适用于数据结构相对稳定的系统,它吧书画家结构和作用于结构上的操作之间的耦合解开,使得操作计划可以相对自由地演化。

目的:把处理从数据结构中分离出来,使得算法操作的增加变得容易。当有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式是比较合适的。

优点:增加新的操作很容易,因为增加新的操作意味着只要增加一个新的访问者,访问者模式将有关的行为集中到一个访问者对象中。

缺点:增加新的数据结构变得困难。大多数情况下很难找到数据结构不变化的情况,因此很少使用访问者模式

给各个设计模式加上UML图及再学习下第一章中UML图怎么使用。

你可能感兴趣的:(大话设计模式随记)