真正意义上北京了三年零四个月了,有时候很无助,有时候很快乐,但大部分时间都是平淡无奇的,一起住了几年的大学室友回家了,怀踹着梦想,怀揣着希望,而我依旧孤独的在战斗,来也匆匆,去也匆匆,没有来的急好好说再见,也没有来得及......
山居秋暝 唐·王维
空山新雨后,天气晚来秋。
明月松间照,清泉石上流。
竹喧归浣女,莲动下渔舟。
随意春芳歇,王孙自可留。
让我也停下来匆匆的,想一想接下来的路该何去何从,接下来该如何抉择,回归平淡?还是做一些改变,想想这几年一路上的不容易,改变了我们当初的模样,想一想在一起的这么长日子,是时候也该让我自己学会一个人学会生活,自己学会坚强,忍受孤独是一个成功人士必备的skills,试着让自己慢慢变的更加坚强,更加努力,更加自信。
简单陈述一下此刻的心情,激励自己在前行的时候不要忘记一下美好的记忆,美好的事情,虽然每个人都会慢慢离开,更古不变,都是有情感,有思维的高级动物,我们不能改变事实,只能改变自己。
回归正题,此刻的我闲下来写一些没有逻辑,没有头脑的东西吧,瞬间感觉有点累了。
分享一下自己对23种设计模式理解:
希望接下来所阐述的观点和语句能够给你们提供些许帮助
大话设计模式:推荐一下这本书,每种设计模式都是一个故事,故事动之以情,晓之以理,让你深入体验到设计模式到好处以及对你“大脑成像”记忆更加直接。
史上最全设计模式导学目录(完整版):https://blog.csdn.net/LoveLion/article/details/17517213?utm_source=blogxgwz0
liuwei写的这篇blog,讲解独到,精准,讲解一些UML类图,源码,以及最后做的总结更加真实。
23种设计模式先用一张图来普及一下:
上图只是找了一张稍微有一些黑暗料理对元素,看起来也很明了了
大话设计模式是这样讲的:
这个设计模式的意图是什么,它要解决一个什么问题,什么时候可以使用它;
它是如何解决的,掌握它的结构图,记住它的关键代码;能够想到至少两个它的应用实例,一个生活中的,一个软件中的;
这个模式的优缺点是什么,在使用时要注意什么。
单例模式
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 - 地基
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 - 楼层柱子结构(钢筋混泥土绑的架子)
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。- 楼建好了,粉刷,装饰
大话设计模式
第一章:代码无错就是优? 简单工厂模式
第二章:商场促销-策略模式
第三章:拍摄UFO-单一指责链原则
第四章:求职考研两不误-开放-封闭原则 (我们在做任何系统的时候,都不要指望系统一开始都是需求确定的,就再也不再变化,这是不现实和不科学的想法,既然需求是一定会变化的,那么如何面对需求的变化,设计软件可以相对应的改变,不致于说,新需求一来,就要把整个程序推倒重来) 多扩展,少修改
古人的理论:管理需要中庸之道
可维护,可扩展,可复用,灵活性好
第五章:会修电脑不会修收音机-依赖倒转原则 (抽象不应该依赖细节,细节应该依赖抽象) 就是要针对接口编程,不要对实现编程)
(高层模块不应该依赖地层模块,两个都应该依赖抽象)
举例:PC里如果CPU、内存、硬盘都需要依赖具体的主板,主板一坏,所有的部件就都没用了,这显然不合理。
所以不论是高层模块还是低层模块,他们都依赖于抽象,具体一点就是接口或抽象类,只要接口稳定,就不用担心收到影响,无论高层模块还是低层模块都可以容易被复用
为什么依赖了抽象接口或者抽象类就不怕更改了嘛?
里氏代替原则:翻译就是一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别,也就是说,在软件里边,把父类都替换成它的子类,程序行为没有变化,简单的说,子类型必须能够替换掉它们的父类型
由于有了里氏替换原则才是开放-封闭成为了可能,由于子类型的可替换性才使得父类型模块在无需修改的情况下就可以扩展,不然还谈什么扩展开放,修改关闭呢,在回头看依赖倒转原则,高层模块不应该依赖地层模块,两个都依赖抽象,就有深刻的理解
依赖倒转其实可以说是面向对象设计的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那几时过成化的谁急了
第六章: 穿什么有这么重要? -装饰模式
动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活
装饰模式:为已有功能动态地添加更多功能的一种方式
优点:把类中的装饰功能从类中搬移去除,这样可以简化原有的类,有效地把类的核心职责和装饰功能区分开了,而且可以去除相关类中重复的装饰逻辑
第七章:为别人做嫁衣 - 代理模式
代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问
代理模式的场景:远程代理,也就是为了一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实
第二种 虚拟代理 ,根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象
第三种 安全代理,用来控制真实对象访问时的权限 一般用于对象应该有不同的访问权限
第四种 智能指引 是指当调用真实的对象时,代理处理另外一些事
第八章 雷锋依然在人间 - 工厂方法模式
简单工厂模式最大的优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖
工厂方法模式(Factory Method) 定义一个用户创建对象的接口,让子类决定实例化哪一个类,工厂方法是一个类的实例化延迟到其子类
工厂方式模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移动了客户端代码进行,你想要加功能,本来时改工厂类的,而现在时修改客户端
工厂方法克服了简单工厂违背开放封闭原则的确定,又保持了封装对象创建过程的优点
第九章 建立复印 - 原型模式
原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。
‘浅复制’: 被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象
‘深复制’:把要复制的对象所引用的对象都复制一遍,也就是说把引用的对象的变量指向复制过的新对象,而不是原有的被引用的对象。
第十章 考题抄错会做也白搭 - 模板方法模式
模板方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
模版方法模式特点: 模板方法模式是通过不变行为搬移到超类,去除子类中的重复代码来体现它的优势
对继承和多态玩的好的人几乎都会在继承体系中多多少少用到它,不如.net或者java类库的设计中,通常都会利用模板方法模式提取类库中的公共行为到抽象类中。
第十一章 无熟人难办事? -迪米特法则
迪米特法则:也叫最少知识原则 如果两个类不必彼此直接通信,那么这两个类就不应当发生直接到相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
迪米特法则其根本思想,是强调了类之间的松耦合(耦合越弱,越有利于复用,一个处在所耦合的类被修改,不会对有关系的类造成波及)
第十二章 牛市股票还会亏钱? - 外观模式
外观模式(Facade) 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
何时用外观模式?
首先,在设计初期阶段,应该要有意识的将不同的两个层分离,比如经典的三层架构,就需要考虑数据访问层和业务逻辑层,业务逻辑层和表示层(mvc)建立外观,这样可以为复杂的子系统提供一个简单的接口,使得耦合大大降低,
其次,在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,提供一个简单的接口,减少它们之间的依赖
第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了
可以为新系统开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。
第十三章 好菜每回味不同 - 建造者模式
建造者模式:又称为生成器模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式
第十四章 老板回来,我不知道 - 观察者模式
观察者模式又叫做发布-订阅(Publish/Subscribe)模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己
观察者模式特点:将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性,我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便
什么时候该用到?
当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
观察者模式所做的工作就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖具体,从而使得各自的变化都不会影响另一边的变化(依赖倒转原则)
事件委托说明:
委托就是一种引用方法的类型,一旦为了委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看作是对函数的抽象,是函数的‘类’,委托的实例将代表一个具体的函数。
一个委托可以搭载多个方法,所有方法被一次唤起,更重要的是,它可以使得委托对象所搭载的方法并不需要属于同一个类
委托对象所搭载的所有方法必须具有相同的原型和形式,也就是拥有相同的参数列表和返回值类型
第十五章 就不能不换DB吗? - 抽象工厂模式
抽象工厂模式 (Abstract Factory): 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂模式的优点与缺点
最大的好处便是易于交换产品系列,由于具体工厂类,在一个应用中只需要初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。
第二大好处,它让具体的创建实例过程于客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现的客户代码中
Assembly.Load("程序集名称").CreateInstance("命名空间.类名称")
在程序顶端加上using System.Reflection;来引用Reflection,就可以使用反射来帮助我们克服抽象工厂模式的先天不足了。
//常规的写法
IUser result = new SqlserverUser();
//反射的写法
using System.Reflection;
IUser result = (IUser)Assembly.Load("抽象工厂模式").CreateInstance("抽象工厂模式.SqlserverUser");
所有在用简单工厂的地方,都可以考虑用反射技术来去除switch 或 if,解除分支判断带来的耦合。
第十六章 无尽加班何时休 - 状态模式
面向对象设计其实就是希望做到代码的责任分解,这个类违背了‘单一职责原则’。
状态模式:
当一个对象的内在的状态改变时允许改变其行为,这个对象看起来像是改变了气类
状态模式主要解决的时当控制一个对象状态转换的条件表达式过于复杂时当情况,把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化
状态模式的好处与用处:状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。 消除庞大的条件分支语句
状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖
什么时候应该考虑使用状态模式呢?
当一个对象的行为取绝于它的状态,并且它必须在运行时刻跟觉状态改变它的行为时,就可以考虑使用状态模式了。
第十七章 在NBA我需要翻译 - 适配器模式
适配器模式:将一个类的接口转换成客户希望另外一个接口,Adapter模式使得原来由于接口不兼容而不能一起工作的那些类可以一起工作
适配器模式主要解决什么问题呢?
简单的说,就是需要的东西就在面前,但却不能使用,而短时间又无法改造它,于是我们就想办法是适配它。
软件开发中,也就是系统的数据和行为都正确,但接口不符合,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。
GoF设计模式:类适配器模式和对象适配器模式
何时使用适配器模式?
使用一个已经存在的类,但如果它的接口,也就是它的方法和你的要求不相同时,就应该考虑用适配器模式?
对的,两个类所做的事情相同或相似,但是具有不同的接口时要使用它,客户代码可以统一调用同一接口,在双方都不容易修改的时候再使用适配器模式适配
适配器模式的.NET应用
DataAdapter用作DataSet和数据源之间的适配器以便检索和保存数据,DataAdpter通过映射Fill和Update来提供这一适配器
NO、NO、NO!模式乱用不如不用,给你讲一个小故事吧,希望你能理解其深意。
扁鹊的医术:魏文王问名医扁鹊说:‘你们家兄弟三人,都精于医术,到底哪一位最好呢?’,长兄最好,中兄次之,我最差。
故事:如果能事先预防接口不同的问题,不匹配问题就不会发生,在有小的接口不统一问题发生时,及时重构,问题不至于扩大,只要碰到无法改变原有设计和代码的情况时,才考虑适配,事后控制不如事中控制,事中控制不如事前控制。
第十八章 如果再回到从前 - 备忘录模式
如果再给我一次机会
备忘录模式也是有缺点的,角色状态需要完整存储到备忘录对象中,如果状态数据很大很多,那么在资源消耗上,备忘录对象会非常耗内存。
第十九章 分公司 = -部门 --- 组合模式
整体与部分可以被一致对待的问题
组合模式:将对象组合成树形结构以表示,‘部分-整体’的层次结构,组合模式实用得用户对单个对象和组合对象的使用具体有一致性
何时使用组合模式呢?
当需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。
组合模式的好处,基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象
简单的说,组合模式让客户可以一致地使用组合结构和当个对象。
第二十章 想走?可以!先买票 - 迭代器模式
迭代器模式(Iterator),提供了一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
当你需要对聚集有多种方式遍历时,可以考虑用迭代器模式
迭代器(Iterator) 模式就是分离了集合对象的遍历行为,抽象出一个迭代器来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
第二十一章 有些类也需计划生育 - 单例模式
单例模式(Singleton)保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
单例模式因为 Singleton类封装它的唯一实例,这样它可以严格地控制客户怎样访问它以及何时访问它。简单的说就是对唯一实例的受控访问。
lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进图锁定的代码,则它将一直等待(即被阻止)直到该对象被释放。
private static readonly Singleton instance = new Singleton();
instance 变量标记readonly,意味着只能在静态初始化期间或者类构造器函数中分配变量,由于这种静态初始化的方式是在自己被加载时就将自己实例化,所以被形象地称之为饿汉式单例类,原先的单例模式处理方式是要在第一次被引用时,才会将自己实例化,所以就被称为懒汉式单例类。
第二十二章 手机软件何时统一 - 桥接模式
合成/聚合复用原则的好处是,优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上,这样类和继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们都可以独立地变化。
核心意图就是把这些实现独立出来,让它们各自地变化,这就使得每种实现的变化不会影响其他实现,从而达到应对变化的目的
只要真正深入地理解了设计原则,很多设计模式其实就是原则的应用而已,或许在不知不觉中就在使用设计模式了。
第二十三章 烤羊肉串引来的思考 - 命令模式
命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队货记录请求日志,以及支持可撤销对操作。
命令模式作用:它能较容易的设计一个命令队列;第二,在需要的情况下,可以较容易地将命令记入日志;第三,允许接受请求的一方决定是否决请求。
第四,可以容易的实现对请求的撤销和重做,第五,由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。关键的优点就是命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分隔开
敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。
第二十四章 加薪非要老总批? --指责链模式
职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
第二十五章 世界需要和平 - 中介者模式
中介者模式(Mediator)用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
中介者模式很容易在系统中应用,也很容易在系统中误用。当系统出现了‘多对多’交互复杂的对象群时,不要急于使用中介者模式,而要先反思你的系统在设计上是不是合理。
第二十六章 项目多也别傻做 - 享元模式
享元模式(Flyweight),运用共享技术有效地支持大量细粒度多对象。
享元模式可以避免大量非常相似类的开销,在程序设计中,有时需要生成大量细粒度多类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够受大幅度地减少需要实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。
什么时候用享元模式?
如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较小的共享对象取代很多组对象,此时可以考虑使用享元模式。
String titleA = "hello world";
String titleB = "hello world";
Console.WriteLine(Object.ReferenceEquals(titleA,titleB));
啊,返回值竟然是True,这两个字符串是相同的实例。
试想一下,如果每次创建字符串对象时,都需要创建一个新的字符串对象的话,内存的开销会很大。如果第一次创建了字符串对象titleA,下次再创建相同字符串titleB时只是把它的引用指向‘大话设计模式’,就这样实现了‘大话设计模式’在内存中的共享。
享元模式更多的时候是一种底层的设计模式,但现实中也是有应用的,如果休闲游戏开发中,像围棋,五子棋,跳棋,她们都有大量的棋子对象,内部状态和外部状态各是什么?
第二十七章 其实你不懂老板的心 - 解释器模式
解释器模式(interpreter),给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
解释器模式需要解决的是,如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子,这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
解释器模式好处
通常当有一个语言需要解释执行,并且你可将该语言中句子表示为一个抽象语法树时,可使用解释器模式。
用了解释器模式,就意味着可以很容易地改变和扩展文法,因为该模式使用类来表示文法规则,你可以使用继承来改变或扩展该文法,也比较容易实现文法,因为定义抽象语法树种中各个节点的类的实现大体类似,这些类都易于直接编写。
使用场景,正则表达式,浏览器
hi,机器人听得懂‘哥们儿’是什么意思?
解释器模式就是将这样一句话,转变成实际的命令程序执行而已,
解释器模式也有不足的,解释器模式为文法中的每一条规则至少定义一个类,因此包含许多规则的文法难以管理和维护。建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理。
第二十八章 男人和女人 - 访问者模式
访问者模式: 表示一个作用雨某个对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
访问者模式的目的是要把处理从数据结构分离出来,很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。
优点:就是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者,访问者模式将有关的行为集中到一个访问者对象中。
缺点:就是增加新的数据结构变得困难了
Gof四人中的一个作者就说过,‘大多时候你并不需要访问者模式,但当一旦你需要访问者模式时,那就是真的需要它了,事实上,我们很难找到数据结构不变化的情况,所以用访问者模式的机会也就不太多了’
比上不足,比下有余
访问者模式比较麻烦,是的,访问者模式的能力和复杂性是把双刃剑,只有当你真正需要它的时候,才考虑使用它。有很多的程序员为了展示自己的面向对象的能力或是沉迷于模式当中,往往会误用这个模式,所以一定要好好理解它的实用性。
综上所述:以上就是对大话设计模式对总结,可能比较单调,接下来,或许你看看具体对一些实现以及一些图形,加深你对理解。
单例模式:
单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:
某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
使用场景:
需要频繁实例化然后销毁的对象
创建对象时耗时过多或者耗资源过多,但又经常用到的对象
有状态的工具类对象
频繁访问数据库或文件的对象
要求生产唯一序列号
WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;而懒汉式在调用取得实例方法的时候才会实例化对象
饿汉式/懒汉
public class Singleton {
public static Singleton singleton = new Singleton();
private Singleton(){}
public Singleton getInstance (){
return singleton;
}
}
线程安全
public class Singleton2 {
/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
public static Singleton2 singleton = null;
/* 私有构造方法,防止被实例化 */
private Singleton2(){}
/* 静态工程方法,创建实例 */
public synchronized Singleton2 getInstance (){
if(singleton == null){
singleton = new Singleton2();
}
return singleton;
}
/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
public Object readResolve() {
return singleton;
}
}
synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降
双检锁/双重校验锁(double-checked locking)
public class Singleton2 {
/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
public static Singleton2 singleton = null;
/* 私有构造方法,防止被实例化 */
private Singleton2(){}
/* 静态工程方法,创建实例 */
public Singleton2 getInstance (){
if(singleton == null){
synchronized(singleton){
if(singleton == null){
singleton = new Singleton2();
}
}
}
return singleton;
}
/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
public Object readResolve() {
return singleton;
}
}
还是会有问题
在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:
a>A、B线程同时进入了第一个if判断
b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。
public class Singleton2 {
/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
public static Singleton2 singleton = null;
/* 私有构造方法,防止被实例化 */
private Singleton2(){}
/* 静态工程方法,创建实例 */
public Singleton2 getInstance (){
if(singleton == null){
syninit();
}
return singleton;
}
private synchronized static void syninit(){
if(singleton == null){
singleton = new Singleton2();
}
}
}
单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。这样我们暂时总结一个完美的单例模式:
静态内部类
public class Singleton3 {
/* 私有构造方法,防止被实例化 */
private Singleton3(){}
/* 此处使用一个内部类来维护单例 */
private static class SingletonHolder{
private static Singleton3 singleton = new Singleton3();
}
/* 此处使用一个内部类来维护单例 */
public Singleton3 getInstance (){
return SingletonHolder.singleton;
}
}
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化
枚举
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
简单,工厂,抽象工厂之间的对比
工厂模式:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。解决接口选择的问题,
应用场景:1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。
适用场景:
1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
简单工厂模式:
适用场景:
在以下情况下可以考虑使用简单工厂模式:
(1) 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
(2) 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。
优缺点:
(1) 工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的职责,而仅仅“消费”产品,简单工厂模式实现了对象创建和使用的分离。
(2) 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以在一定程度减少使用者的记忆量。
(3) 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
(1) 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响。
(2) 使用简单工厂模式势必会增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度。
(3) 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
(4) 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
抽象工厂模式:
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
适用场景:
1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
下面例子中鼠标,键盘,耳麦为产品,惠普,戴尔为工厂。
简单工厂模式
简单工厂模式不是 23 种里的一种,简而言之,就是有一个专门生产某个产品的类。
比如下图中的鼠标工厂,专业生产鼠标,给参数 0,生产戴尔鼠标,给参数 1,生产惠普鼠标。
工厂模式
工厂模式也就是鼠标工厂是个父类,有生产鼠标这个接口。
戴尔鼠标工厂,惠普鼠标工厂继承它,可以分别生产戴尔鼠标,惠普鼠标。
生产哪种鼠标不再由参数决定,而是创建鼠标工厂时,由戴尔鼠标工厂创建。
后续直接调用鼠标工厂.生产鼠标()即可
抽象工厂模式
抽象工厂模式也就是不仅生产鼠标,同时生产键盘。
也就是 PC 厂商是个父类,有生产鼠标,生产键盘两个接口。
戴尔工厂,惠普工厂继承它,可以分别生产戴尔鼠标+戴尔键盘,和惠普鼠标+惠普键盘。
创建工厂时,由戴尔工厂创建。
后续工厂.生产鼠标()则生产戴尔鼠标,工厂.生产键盘()则生产戴尔键盘。
在抽象工厂模式中,假设我们需要增加一个工厂
假设我们增加华硕工厂,则我们需要增加华硕工厂,和戴尔工厂一样,继承 PC 厂商。
之后创建华硕鼠标,继承鼠标类。创建华硕键盘,继承键盘类即可。
在抽象工厂模式中,假设我们需要增加一个产品
假设我们增加耳麦这个产品,则首先我们需要增加耳麦这个父类,再加上戴尔耳麦,惠普耳麦这两个子类。
之后在PC厂商这个父类中,增加生产耳麦的接口。最后在戴尔工厂,惠普工厂这两个类中,分别实现生产戴尔耳麦,惠普耳麦的功能。 以上。
一、一句话概括工厂模式
简单工厂:一个工厂类,一个产品抽象类。
工厂方法:多个工厂类,一个产品抽象类。
抽象工厂:多个工厂类,多个产品抽象类。
二、生活中的工厂模式
简单工厂类:一个麦当劳店,可以生产多种汉堡。
工厂方法类:一个麦当劳店,可以生产多种汉堡。一个肯德基店,也可以生产多种汉堡。
抽象工厂类:百胜餐饮集团下有肯德基和百事公司,肯德基生产汉堡,百事公司生成百事可乐。
为了更能体验工厂之间的对比,可以看接下来几组图片:可能给你以灵感
原型模式:
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
主要解决:在运行期建立和删除原型。
应用实例: 1、细胞分裂。 2、JAVA 中的 Object clone() 方法。
优点: 1、性能提高。 2、逃避构造函数的约束。
缺点: 1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。
适用场景:1、资源优化场景。 2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 3、性能和安全要求的场景。 4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 5、一个对象多个修改者的场景。 6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
建造者模式:
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
适用场景:去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的 StringBuilder。
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
适配器模式: @
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
何时使用:
1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
实例:
1、美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。 2、JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。 3、在 LINUX 上运行 WINDOWS 程序。 4、JAVA 中的 jdbc。
优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。
缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
桥接模式: @
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
解决:将抽象部分与实现部分分离,使它们都可以独立的变化。在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
优点: 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。
缺点:桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
使用场景: 1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
过滤器模式:
过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。
过滤模式的实现在java8里面有典型的应用方法就是分组操作,可以根据指定的指标进行分组筛选。
Map
groupMap.forEach((k, v) -> {
System.out.println(k);
v.forEach(System.out::println);
});
得到的结果形式就是:
k:是分组的指标,上面代码中的 gender
v:是一个list的集合对象,就是 personList
组合模式:
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
解决:组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
何时使用:
1、您想表示对象的部分-整体层次结构(树形结构)。 2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
使用实例:
1、算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作符也可以是操作数、操作符和另一个操作数。 2、在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。
优点: 1、高层模块调用简单。 2、节点自由增加。
缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。
装饰器模式:
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
应用实例:
1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
一个更易理解的实例:
装饰模式为已有类动态附加额外的功能就像LOL、王者荣耀等类Dota游戏中,英雄升级一样。每次英雄升级都会附加一个额外技能点学习技能。具体的英雄就是ConcreteComponent,技能栏就是装饰器Decorator,每个技能就是ConcreteDecorator;
外观模式:
外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。
何时使用:1、客户端不需要知道系统内部的复杂联系,整个系统只需提供一个"接待员"即可。 2、定义系统的入口。
应用实例:
1、去医院看病,可能要去挂号、门诊、划价、取药,让患者或患者家属觉得很复杂,如果有提供接待人员,只让接待人员来处理,就很方便。 2、JAVA 的三层开发模式。
优点: 1、减少系统相互依赖。 2、提高灵活性。 3、提高了安全性。
缺点:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
使用场景: 1、为复杂的模块或子系统提供外界访问的模块。 2、子系统相对独立。 3、预防低水平人员带来的风险。
享元模式:
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
何时使用:
1、系统中有大量对象。 2、这些对象消耗大量内存。 3、这些对象的状态大部分可以外部化。 4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。 5、系统不依赖于这些对象身份,这些对象是不可分辨的。
如何解决:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。
应用实例: 1、JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。 2、数据库的数据池。
优点:大大减少对象的创建,降低系统的内存,使效率提高。
缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景。
注意事项: 1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。 2、这些类必须有一个工厂对象加以控制。
代理模式: @
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
主要解决:
在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
如何解决:增加中间层
应用实例:
1、Windows 里面的快捷方式。 2、猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。 3、买火车票不一定在火车站买,也可以去代售点。 4、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。 5、spring aop。
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景:
按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。
区别:1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
JDK 自带的动态代理
java.lang.reflect.Proxy:生成动态代理类和对象;
java.lang.reflect.InvocationHandler(处理器接口):可以通过invoke方法实现
对真实角色的代理访问。
每次通过 Proxy 生成的代理类对象都要指定对应的处理器对象。
责任链模式: @
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。
意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
何时使用:在处理消息的时候以过滤很多道。
何时解决:拦截的类都实现统一接口。
应用实例:
1、JS中的冒泡,
2、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。
优点:
1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
缺点:
1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
使用场景: 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。
命令模式: @
命令模式(Command Pattern)是一种数据驱动的设计模式,请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。
使用场景:认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。
解释器模式:@
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
主要解决:对于一些固定文法构建一个解释句子的解释器。
优点: 1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。
缺点: 1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。
使用场景: 1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 2、一些重复出现的问题可以用一种简单的语言来进行表达。 3、一个简单语法需要解释的场景。
迭代器模式:
迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
主要解决:不同的方式来遍历整个整合对象
应用实例:JAVA中的Iterator
优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
中介者模式:@
中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性;
主要解决:
对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
应用实例: 1、中国加入 WTO 之前是各个国家相互贸易,结构复杂,现在是各个国家通过 WTO 来互相贸易。 2、机场调度系统。 3、MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。
优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。
缺点:中介者会庞大,变得复杂难以维护。
备忘录模式:
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象;
主要解决:
所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
应用实例: 1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctri + z。 4、IE 中的后退。 4、数据库的事务管理。
优点: 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
使用场景: 1、需要保存/恢复数据的相关状态场景。 2、提供一个可回滚的操作。
观察者模式: @
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
1、JAVA 中已经有了对观察者模式的支持类(java.util.Observable)。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
发布/订阅
状态模式:@
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
意图: 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
何时使用:代码中包含大量与对象状态有关的条件语句。
应用实例: 1、打篮球的时候运动员可以有正常状态、不正常状态和超常状态。
优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景: 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。
策略模式:
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
模板模式:@
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
主要解决:
一些方法通用,却在每一个子类都重新写了这一方法。
应用实例: 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。 2、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。 3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
访问者模式:
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。
意图:主要将数据结构与数据操作分离。
何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。