23种设计模式

来源:互联网 发布:淘宝新手入门教程视频 编辑:程序博客网 时间:2024/06/05 23:33

一、单例模式

1. 定义

2. 使用场景

3. 类图

二、工厂模式

1. 定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

2. 使用场景

1)工厂方法模式是new一个对象的替代品,所以在所有生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。

2)需要灵活的、可扩展的框架时,例如需要设计一个连接邮件服务器的框架,有三种网络协议可选择:POP3、IMAP、HTTP,我们就可以把这三种连接方法作为产品类,定义一个接口如IConnectMail,然后定义对邮件的操作方法,用不同的方法实现 三个具体的产品类(也就是连接方式)再定义一个工厂方法,按照不同的传入条件,选择不同的连接方式。

3. 类图


三、抽象工厂模式

1. 定义

为创建一组或相互依赖的对象提供接口,而无需指定具体类。

2. 使用场景

1)一个对象族(或一组没有关系的对象)有相同的约束,则可以使用抽象工厂模式。比如:某个文本编辑器在不同系统中虽然界面一样,但是内部代码不同,于是我们就可编写多个工厂类分别调用。

3. 类图


4. 提示

在场景类中,没有任何一个方法与实现类有关系,对于一个产品来说,我们只要知道它的工厂方法就可以直接生产一个产品对象,无须关心它的实现类。

四、模板方法模式

1. 定义

定义一个操作中的算法的骨架,而将步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤。

2. 使用场景

1)多个子类有公有的方法,并且逻辑基本相同时。

2)重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。

3)重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数(见扩展)约束其行为

3. 类图


4. 扩展

run()方法可以按照规定的顺序执行,也可以由isAlarm()方法的返回值影响run()中的执行顺序。

五、建造者模式

1. 定义

将一个复杂对象的创建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式的核心:组装顺序不同对象效能不同。(内置模板方法模式)

2. 使用场景

1)相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式。

2)多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。

3)产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这里使用建造者模式非常合适

3. 类图

clip_image008

4. 扩展

建造者模式和工厂模式的区别:

建造者模式最主要功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了,通俗地说就是零件的装配,顺序不同产生的对象也不同。

工厂模式重点就是创建,创建零件是它的主要职责,组装顺序不是它关心的。

六、代理模式

1. 定义

为其它对象提供一种代理以控制对这个对象的访问。

2. 使用场景

我相信第一次接触代理模式的读者肯定很郁闷,为什么要用代理呀?想想我们现实世界吧,打官司为什么要找个律师?因为你不想参与中间过程的是是非非,只要完成自己的啊答辩就成,其他的比如事前调查、事后追查都由律师来搞掂,这就是为了减轻你的负担。代理模式的使用场景非常多,大家可以看看Spring AOP,这是一个非常典型的动态代理。

3. 类图


七、原型模式

1. 定义

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

2. 使用场景

1)类初始化需要消化非常多的资源,这个资源包括数据,硬件资源。

2)通过new产生一个对象需要非常繁的数据准备或访问权限,则可以使用原型模式。

3)一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。

在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与Java融为一体,大家可以随手拿来使用。

3. 类图


4. 扩展

原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,—— 其实是重写的Object类的clone方法!Object类的clone方法的原理是从内存中(具体地说就是堆内存)以二进制流的方式进行拷贝,重新分配一个内存块,那构造函数就不会被执行。这种不通过new关键字来产生一个对象,而是通过对象复制来实现的模式就叫做原型模式。

八、中介者模式

1. 定义

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

2. 使用场景

中介者并非是有多个类的依赖就要用中介类模式,如果强行加入,会让中介者的逻辑复杂化。

中介者模式适用于多个对象之间紧密耦合的情况,紧密耦合的标准是:在类图中出现了蜘蛛网状结构,这种情况下一定考虑中介者模式,有利于把蜘蛛网梳理为星型结构,使原本复杂混乱的关系变得清晰简单。

3. 类图


4. 扩展

中介者模式适用于多个对象之间紧密耦合的情况,紧密耦合的标准是:在类图中出现了蜘蛛网状结构,这种情况下一定考虑中介者模式,有利于把蜘蛛网梳理为星型结构,使原本复杂混乱的关系变得清晰简单。

九、命令模式

1. 定义

将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

2. 使用场景

只要你认为是命令的地方就可以采用命令模式,例如,GUI开发中,一个按钮的点击是一个命令,可以采用命令模式;模拟DOS命令时候,也可以采用命令模式;触发-反馈机制的处理等。

3. 类图


十、责任链模式

1. 定义

使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理为止。

2. 使用场景

3. 类图


十一、装饰模式

1. 定义

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。

2. 使用场景

1)需要扩展一个类的功能,或给一个类增加附加功能

2)需要动态地给一个对象增加功能,这些功能可以再动态地撤销。

3)需要为一批兄弟类进行改装或加装功能,当然是首先装饰模式。

3. 类图


十二、策略模式

1. 定义

定义一组算法,将每个算法都封装起来,使它们之间可以互换。策略模式代替if-else,传什么类型过去,就执行什么策略。

2. 使用场景

1)多个类只有在算法或行为上稍有不同的场景

2)算法需要自由切换的场景

3)需要屏蔽算法 规则的场景 

3. 类图


十三、适配器模式

1. 定义

将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

2. 使用场景

记住一点:你有动机修改一个已经投产中的接口时,适配器模式可能 最适合你的模式,比如:系统扩展了,需要使用一个已有或新建立的类,但这个类又不符合系统接口,这里可以使用适配器模式。

适配器模式是解决正在服役的项目问题,主要场景是扩展应用,没有设计师在设计阶段考虑适配器。

3. 类图


4. 扩展

适配器可方便完成系统对接,实际系统中只是增加了一个业务类的继承,就实现 了可以查本公司的员工信息,也可以查到人力资源公司的员工信息。

十四、迭代器模式

1. 定义

它提供一种方法访问一个容器对象中各个元素,而又不需要暴露该对象的内部细节。

2. 使用场景

迭代器是为容器(Collection/List/Set)服务的,提供了遍历容器的方便性。尽量不用自己开发迭代器模式,因为Java提供的Iterator应用到各个聚集类(Collection)中,基本满足需要

3. 类图


十五、组合模式

1. 定义

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

2. 使用场景

1)维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。

2)从一个整体中能够独立出部分模块或功能的场景。

3. 类图


十六、观察者模式

1. 定义

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

2. 使用场景

1)关联行为场景,需要注意的是,关联行为是可以拆分的,而不是组合关系。

2)事件多级触发场景

3)跨系统的消息交换场景,如消息队列 的处理机制

3. 类图


4. 扩展

推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能提供新的update()方法,或者是干脆重新实现观察者;

而拉模型就不会造成这样的情况,因为拉模型下,update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要。 —— 工作中一般使用拉模式,因为可以拉取整个主题对象

十七、门面模式

1. 定义

要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行,门面模式提供一个高层次的接口,使得子系统更易于使用。

2. 使用场景

1)为一个复杂的模块或子系统提供一个供外界访问的接口

2)子系统相对独立——外界对子系统的访问只要黑箱操作即可,比如:利息计算

3)预防低水平人员带来的风险扩散,只能在指定的子系统 中开发,然后再提供门面接口进行访问操作

3. 类图


4. 扩展

门面对象只是提供一个访问子系统的一个路径而已,它不应该不能参与具体的业务逻辑,把多变的业务逻辑的变化封闭在子系统内部,无论你如何变化,对外界的访问者来说,都还是同一个门面(只提供路径)。否则就会产生一个倒依赖的问题:子系统必须依赖门面才能访问,这是设计上的错误。

十八、备忘录模式

1. 定义

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

2. 使用场景

1)需要保存和恢复数据的相关状态场景

2)提供一个类似可回滚(rollback)操作

3)需要监控的副本场景中。例如要监控一个对象属性,但是监控又不应该作为系统的主业务来调用,它只是边缘应用,即使出现监控不准、错误报警也影响不大,因此一般的做法是备份一个主线程中的对象,然后由分析程序来分析

3. 类图


4. 扩展

创建备忘录:备忘录由发起人创建后,set进备忘录管理类

恢复备忘录:从备忘录管理者中get到,后恢复到发起人中。

十九、访问者模式

1. 定义

封装一些作用于某种数据结构(List/Set/Map)中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

2. 使用场景

1)一个对象结构(List/Set/Map等)包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,也就是说迭代器模式已经不能胜任的情景。

2)需要对一个对象结构(List/Set/Map等)中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。

3. 类图


二十、状态模式

1. 定义

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

2. 使用场景

1)行为随状态改变而改变的场景

2)条件、分支判断语句的替代者

3. 类图


二十一、解释器模式

1. 定义

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

2. 使用场景

1)重复发生的问题可以使用解释器模式

例如,多个应用服务器,每天产生大量的日志,需要对日志文件进行分析处理,由于各个服务器的日志格式不同,但是数据要素是相同的,按照解释器的说法是终结符表达式都是相同的,但是非终结符表达式就需要制定了。在这种情况下,可以通过程序来一劳永逸的解决该问题。

2)一个简单语法需要解释的场景

例如,非终结表达式不多的情况

3. 类图


4. 扩展

解释器模式在开发中应用很少,它会引起效率、性能以及维护等问题,可以考虑用Expression4J、MESP、Jep等开源解析工具包,实现大多数数学运算完全没有问题。

二十二、享元模式

1. 定义

使用共享对象可有效地支持大量的细粒度的对象。运用共享技术,使得一些细粒度的对象可以共享。

2. 使用场景

1) 系统中存在大量的相似的对象

2) 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份。

3) 需要缓冲池的场景

3. 类图


4. 扩展

1)池中设置的享元对象数量太少,导致每个线程都到对象池中获得对象,然后都去修改其属性,于是就出现一些不和谐的数据

2)外部状态最好以Java的基本类型作为标志,如String、int等,可以大幅提升效率

二十三、桥梁模式

1. 定义

将抽象和实现解耦,使得两者可以独立地变化。

抽象角色引用实现角色,或者说抽象角色的部分实现是由实现角色完成的。当发现类的继承有N层时,可以考虑使用桥梁模式。

2. 使用场景

1)不希望或不适用使用继承的场景

例如继承层次过渡、无法更加细化设计颗粒等场景,需要考虑使用桥梁模式

2)接口或抽象类不稳定的场景

明知道接口不稳定还想通过实现或继承来实现业务需求,那是得不偿失的,也是比较失败的做法。

3)重用性要求较高的场景

设计的颗粒度越细,则被重用的可能性就越大,而采用的继承则受父类的限制,不可能出现太细的颗粒度。

3. 类图


原创粉丝点击