23种设计模式全

初学设计模式,建议可以参考菜鸟教程,里面讲的很详细,后来发现那里也是从国外的翻译过来的hhh,这里我总结一下常见的23种设计模式。

一、设计模式的六大原则:

1、开闭原则(Open Close Principle)

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类等。

2、里氏替换原则(Liskov Substitution Principle)

面向对象设计的基本原则之一,是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。在进行设计的时候,我们应该尽量从抽象类继承,而不是从具体类继承.如果从继承等树来看,所有叶子节点应当是具体类,而所有的树枝节点应当是抽象类或者接口.当然这只是一个一般性的指导原则,使用的时候还要具体情况具体分析。

3、依赖倒转原则(Dependence Inversion Principle)

这个是面向对象设计的核心原则,具体内容:针对接口编程,不针对实现编程。依赖于抽象而不依赖于具体。写代码用到具体类时,不与具体类交互,而与具体类的上层接口交互。

4、接口隔离原则(Interface Segregation Principle)

每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。

5、迪米特法则(最少知道原则)(Demeter Principle)

一个类对自己依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。即一个软件实体应当尽可能少地与其他实体发生相互作用,通过中间类建立联系。

6、合成复用原则(Composite Reuse Principle)

原则一句话:是尽量首先使用合成/聚合的方式,而不是使用继承。

二、创建型模式(5种)

简单工厂模式:

简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,它不属于23种GOF设计模式,但是可以被认为是一种特殊的工厂方法模式,在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有相同的父类。

应用:

工厂方法模式:

简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到工厂方法模式,在简单工厂中,创建对象的是另一个类,而在工厂方法中,工厂父类负责创建对象的公共接口,工厂子类来创建具体的对象。
在这里插入图片描述
适用场景:

  • 客户不需要知道要使用的对象的创建过程
  • 客户使用的对象存在变动的可能,或者根本就不知道使用哪一个具体对象

优缺点:

  • 一旦需要增加新的功能,直接增加具体工厂和具体产品就可以了,不需要修改之前的代码。
  • 增加新产品的同时需要增加新的工厂,导致系统类的个数成对增加,一定程度上增加了系统复杂性。

应用:

抽象工厂模式:
  • 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。

  • 一个抽象工厂类,可以派生出多个具体工厂类。

  • 每个具体工厂类可以创建多个具体产品类的实例。

  • 工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。

  • 工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

  • 在这里插入图片描述

适用场景:

  • 系统中有多个产品族,而系统一次只能消费其中一族产品
  • 同属于同一个产品族的产品一起使用

优缺点:

  • 新增产品族很方便,只需要增加一个具体工厂即可。
  • 但如果要增加新产品等级结构,就需要修改抽象工厂和所有具体工厂类,这种性质被称为 开闭原则 的倾斜性。
单例模式:

在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。

单例模式的实现有懒汉式(线程不安全)、饿汉式(线程安全,一开始就直接实例化),静态内部类实现等。

静态内部类实现:
使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,避免出现 JVM 具有指令重排的特性。

public class Singleton {
 
	/* 私有构造方法,防止被实例化 */
	private Singleton() {
	}
 
	/* 此处使用一个内部类来维护单例 */
	private static class SingletonFactory {
		private static Singleton instance = new Singleton();
	}
 
	/* 获取实例 */
	public static Singleton getInstance() {
		return SingletonFactory.instance;
	}
 
	/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
	public Object readResolve() {
		return getInstance();
	}

建造者模式:

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。
主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

如Java中的StringBuilder类的实现就是应用这种模式。

原型模式:

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

关键代码:实现克隆操作,在 JAVA 实现 Cloneable,重写 clone();

优点:

  • 性能提高。
  • 逃避构造函数的约束。

缺点:

  • 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
  • 必须实现 Cloneable 接口。

三、结构型模式(7种)

适配器模式:

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁,它结合了两个独立接口的功能。如读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

方法:适配器继承或依赖已有的对象,实现想要的目标接口。

优点:

  • 可以让任何两个没有关联的类一起运行。
  • 提高了类的复用。
  • 增加了类的透明度。

缺点:

  • 过多地使用适配器,会让系统非常零乱,不易整体进行把握。
  • 因 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

应用:java.util.Arrays#asList()

桥接模式:

将抽象和实现放在两个不同的类层次中,使它们可以独立地变化。——《Head First 设计模式》

将类的功能层次结构和实现层次结构相分离,使二者能够独立地变化,并在两者之间搭建桥梁,实现桥接。—— 《图解设计模式》

我们常用的JDBC桥DriverManager就是如此,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。
在这里插入图片描述

组合模式:

组合模式(Composite Pattern),是用于把一组相似的对象当作一个单一的对象。将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。

优点:

  • 高层模块调用简单。
  • 节点自由增加。

缺点:

  • 在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。

应用:(组合模式的使用场景就是出现树形结构的地方)

  • 文件目录显示,多及目录呈现等树形结构数据的操作
  • java.util.Map#putAll(Map)
  • java.util.List#addAll(Collection)
  • java.util.Set#addAll(Collection)
装饰者模式:

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。要求装饰对象和被装饰对象实现同一个接口。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点:多层装饰比较复杂。

使用场景:

  • 扩展一个类的功能。
  • 动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
外观模式:

外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。

优点:

  • 减少系统相互依赖。
  • 提高灵活性。
  • 提高了安全性。

缺点:

  • 不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。

使用场景:

  • JAVA 的三层开发模式。
  • 假设一台电脑,它包含了 CPU,Memory ,Disk)这几个部件,若想要启动电脑,则先后必须启动 CPU、Memory、Disk。关闭也是如此。
    但是实际上我们在电脑开/关机时根本不需要去操作这些组件,因为电脑已经帮我们都处理好了,并隐藏了这些东西。
    这些组件好比子系统角色(一个系统的子系统或模块),而电脑就是一个外观角色(客户端通过操作外观角色从而达到控制子系统角色的目的)。
享元模式:

享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。

在这里插入图片描述

FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象。

优点:大大减少对象的创建,降低系统的内存,使效率提高。

缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

应用:

  • 1、JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。
  • 2、数据库连接池,不需要每一次都重新创建连接,节省了数据库重新创建的开销。
  • java.lang.Integer.valueOf()
代理模式:

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能,我们创建具有现有对象的对象,以便向外界提供功能接口,为其他对象提供一种代理以控制对这个对象的访问。比如我们在租房子的时候回去找中介,因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。

优点:

  • 职责清晰。
  • 高扩展性。
  • 智能化。

缺点:

  • 1、客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
  • 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

应用:

  • java.lang.reflect.Proxy
  • Spring Aop

注意:

  • 区别适配器模式:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
  • 区别装饰器模式:装饰器模式为了增强功能,而代理模式是为了加以控制。

四、行为型模式(11种)

责任链模式:

责任链模式(Chain of Responsibility Pattern),为请求创建了一个接收者对象的链。有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求。
在这里插入图片描述

优点:

  • 降低耦合度。它将请求的发送者和接收者解耦。
  • 简化了对象。使得对象不需要知道链的结构。
  • 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
  • 增加新的请求处理类很方便。

缺点:

  • 不能保证请求一定被接收。
  • 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
  • 可能不容易观察运行时的特征,有碍于除错。

应用:

  • java.util.logging.Logger.log()
  • Apache Commons Chain
  • javax.servlet.Filter.doFilter()
  • Java Web 中 Apache Tomcat 对 Encoding 的处理
命令模式:

命令模式(Command Pattern)把一个请求或者操作封装到一个对象中,把发出命令的责任和执行命令的责任分割开,委派给不同的对象,从使用角度来看就是请求者把接口实现类作为参数传给使用者,使用者直接调用这个接口的方法,而不用关心具体执行的那个命令。
优点:

  • 降低了系统耦合度。
  • 新的命令可以很容易添加到系统中去。

缺点:

  • 使用命令模式可能会导致某些系统有过多的具体命令类。

应用:java.lang.Runnable

解释器模式:

解释器模式(Interpreter Pattern)实现了一个表达式接口,该接口解释一个特定的上下文。给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
优点:

  • 可扩展性比较好,灵活。
  • 增加了新的解释表达式的方式。
  • 易于实现简单文法。

缺点:

  • 可利用场景比较少。
  • 对于复杂的文法比较难维护。
  • 解释器模式会引起类膨胀。
  • 解释器模式采用递归调用方法。

使用场景:

  • 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  • 一些重复出现的问题可以用一种简单的语言来进行表达。
  • 一个简单语法需要解释的场景。
迭代器模式:

迭代器模式(Iterator Pattern)提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

优点:

  • 它支持以不同的方式遍历一个聚合对象。
  • 迭代器简化了聚合类。
  • 在同一个聚合上可以有多个遍历。
  • 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。

缺点:

  • 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

应用:Java.util.Iterator

中介者模式:

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

优点:

  • 降低了类的复杂度,将一对多转化成了一对一。
  • 各个类之间的解耦。
  • 符合迪米特原则。

缺点:

  • 中介者会庞大,变得复杂难以维护。

应用:

  • 系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。
  • 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
  • MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。
  • java.lang.reflect.Method.invoke()。
备忘录模式:

备忘录模式(Memento Pattern),保存一个对象的某个状态,以便在适当的时候恢复对象。在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。

优点:

  • 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
  • 2、实现了信息的封装,使得用户不需要关心状态的保存细节。

缺点:

  • 消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

应用:

  • 需要保存/恢复数据的相关状态场景。
  • 数据库事务回滚。
观察者模式:

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern),又叫发布-订阅模式, 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。类似于邮件订阅,当你订阅了该文章,如果后续有更新,会及时通知你。

优点:

  • 观察者和被观察者是抽象耦合的。
  • 建立一套触发机制。

缺点:

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
状态模式:

在状态模式(State Pattern)中,类的行为是基于它的状态改变的,当对象的状态改变时,同时改变其行为,就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态。

优点:

  • 封装了转换规则。
  • 枚举可能的状态,在枚举状态之前需要确定状态种类。
  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点:

  • 状态模式的使用必然会增加系统类和对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
  • 状态模式对"开闭原则"的支持并不太好。

使用场景:

  • 行为随状态改变而改变的场景。
  • 条件、分支语句的代替者。
策略模式:

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。需要定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

优点:

  • 算法可以自由切换。
  • 避免使用多重条件判断。
  • 扩展性良好。

缺点:

  • 策略类会增多。
  • 所有策略类都需要对外暴露。

区分状态模式:
状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。

状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。

应用:
java.util.Comparator.compare()
javax.servlet.http.HttpServlet
javax.servlet.Filter.doFilter()

模板方法模式:

在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

优点:

  • 封装不变部分,扩展可变部分。
  • 提取公共代码,便于维护。
  • 行为由父类控制,子类实现。

缺点:

  • 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

应用:

  • 有多个子类共有的方法,且逻辑相同。
  • java.util.Collections.sort()。
访问者模式:

在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

优点:

  • 符合单一职责原则。
  • 优秀的扩展性。
  • 灵活性。

缺点:

  • 具体元素对访问者公布细节,违反了迪米特原则。
  • 具体元素变更比较困难。
  • 违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

应用:
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。

补充:

参考资料:

  • 《设计模式》清华大学出版社刘伟主编
  • 菜鸟教程
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值