Java 设计模式(java design patterns)
设计模式,是一被反复使用、多数知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题法人解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定普遍性,可以反复使用。其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。
设计模式:https://www.runoob.com/design-pattern/design-pattern-tutorial.html
学习设计模式的意义
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性 和多态性以及类的关联关系和组合关系的充分理解。正确使用设计模式具有以下优点。
- 可以提高程序员的思维能力、编程能力和设计能力。
- 使程序设计更加标准化、代码编制更加工程化,使开发效率大大提高, 从而缩短软件的开发周期。
- 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
Java设计模式类型
根据模式是用来完成什么工作来划分,这种方式可分为创建型模式、结构型 模式和行为型模式 3 种。
创建型模式: 用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使 用分离”。提供了单例、原型、工厂方法、抽象工厂、建造者 5 种创建型模式。
结构型模式: 用于描述如何将类或对象按某种布局组成更大的结构,提供了代理、 适配器、桥接、装饰、外观、享元、组合 7 种结构型模式。
行为型模式: 用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独 完成的任务,以及怎样分配职责。提供了模板方法、策略、命令、职责链、状态、 观察者、中介者、迭代器、访问者、备忘录、解释器 11 种行为型模式。
23种设计模式介绍
- 单例(Singleton)模式: 某个类只能生成一个实例,该类提供了一个全局 访问点供外部获取该实例,其拓展是有限多例模式。
- 原型(Prototype)模式: 将一个对象作为原型,通过对其进行复制而克隆 出多个和原型类似的新实例。
- 工厂方法(Factory Method)模式: 定义一个用于创建产品的接口,由子类决定生产什么产品。
- 抽象工厂(AbstractFactory)模式: 提供一个创建产品族的接口,其每个 子类可以生产一系列相关的产品。
- 建造者(Builder)模式: 将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
- 代理(Proxy)模式: 为某对象提供一种代理以控制对该对象的访问。即客 户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
- 适配器(Adapter)模式: 将一个类的接口转换成客户希望的另外一个接口, 使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
- 桥接(Bridge)模式: 将抽象与实现分离,使它们可以独立变化。它是用组 合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
- 装饰(Decorator)模式: 动态的给对象增加一些职责,即增加其额外的功能。
- 外观(Facade)模式: 为多个复杂的子系统提供一个一致的接口,使这些子 系统更加容易被访问。
- 享元(Flyweight)模式: 运用共享技术来有效地支持大量细粒度对象的复用。
- 组合(Composite)模式: 将对象组合成树状层次结构,使用户对单个对象 和组合对象具有一致的访问性。
- 模板方法(TemplateMethod)模式: 定义一个操作中的算法骨架,而将算 法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定 义该算法的某些特定步骤。
- 策略(Strategy)模式: 定义了一系列算法,并将每个算法封装起来,使它 们可以相互替换,且算法的改变不会影响使用算法的客户。
- 命令(Command)模式: 将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
- 职责链(Chain of Responsibility)模式: 把请求从链中的一个对象传到下 一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
- 状态(State)模式: 允许一个对象在其内部状态发生改变时改变其行为能力。
- 观察者(Observer)模式: 多个对象间存在一对多关系,当一个对象发生改 变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
- 中介者(Mediator)模式: 定义一个中介对象来简化原有对象之间的交互 关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
- 迭代器(Iterator)模式: 提供一种方法来顺序访问聚合对象中的一系列数 据,而不暴露聚合对象的内部表示。
- 访问者(Visitor)模式: 在不改变集合元素的前提下,为一个集合中的每个 元素提供多种访问方式,即每个元素有多个访问者对象访问。
- 备忘录(Memento)模式: 在不破坏封装性的前提下,获取并保存一个对 象的内部状态,以便以后恢复它。
- 解释器(Interpreter)模式: 提供如何定义语言的文法,以及对语言句子的 解释方法,即解释器。
常用设计模式
单例模式
在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。例如,Windows中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。
单例模式有3个特点:
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点;
单例模式的结构
单例类:包含一个实例且能自行创建这个实例的类。
访问类:使用单例的类。其结构如图所示:
![Java的设计模式_第1张图片](http://img.e-com-net.com/image/info8/9ebcfcfd768845c5a873545f4e027a94.jpg)
Singleton 模式通常有两种实现形式。
第一种:懒汉式单例
该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法 时才去创建这个单例。
第 2 种:饿汉式单例
该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之 前单例已经存在了。
工厂模式(Factory Pattern)
工厂模式的定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工 作推迟到工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
按实际业务场景划分,工厂模式有 2 种不同的实现方式,分别是简单工厂模式、 抽象工厂模式。
简单工厂
我们把创建的对象称为“产品”,把创建产品的对象为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern) 又叫作静态工厂方法模式(Static Factory Method Pattern)。
简单工厂模式的主要角色如下:
简单工厂(SimpleFactory): 是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
抽象产品(Product): 是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
具体产品(ConcreteProduct): 是简单工厂模式的创建目标。
其结构图如下图所示。
![Java的设计模式_第2张图片](http://img.e-com-net.com/image/info8/5f191b3be2e54e7182223d7d5347b515.jpg)
代理模式
在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一 个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要 去火车站买,可以通过 12306 网站或者去火车票代售点买。又如找保姆、找工 作,买东西等都可以通过找中介完成。
代理模式的主要优点有:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
模式的结构
- 抽象主题(Subject)类: 通过接口或抽象类声明真实主题和代理对象实现 的业务方法。
- 真实主题(Real Subject)类: 实现了抽象主题中的具体业务,是代理对象 所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类: 提供了与真实主题相同的接口,其内部含有对真实主题 的引用,它可以访问、控制或扩展真实主题的功能。
其结构图如图所示。
![Java的设计模式_第3张图片](http://img.e-com-net.com/image/info8/e78afd6793684b08ad710d0b946ea64b.jpg)
代理实现可以分为静态代理和动态代理。
静态代理
静态代理模式的特点,代理类接受一个Subject接口的对象,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。但是也有缺点,每一个代理类都必须实现一遍委托类(也就是 realsubject)的接口,如果接口增加方法,则代理类也必须跟着修改。其次,代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理类就非常臃肿,难以胜任。
动态代理
动态代理中,代理类并不是在 Java 代码中实现,而是在运行时期生成,相 比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法 调用次数、添加日志功能等等,动态代理分为 jdk 动态代理和 cglib 动态代理。
JDK代理
动态代理是实现方式,是通过反射来实现的,借助 Java 自带的 java.lang.reflect.Proxy,通过固定的规则生成。
其步骤如下:
- 编写一个委托类的接口,即静态代理的
- 实现一个真正的委托类,即静态代理的
- 创建一个动态代理类,实现 InvocationHandler 接口,并重写该 invoke 方法
- 在测试类中,生成动态代理的对象。
动态代理总结: 虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface 代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫 Proxy。
Cglib 代理
JDK 实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何 实现动态代理呢,这就需要 CGLib 了。CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦 截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对 final 修饰的类进行代理。JDK 动态代理与 CGLib 动态代理均是实现 Spring AOP 的基础。
Cglib 子类代理实现方法:
- 需要引入 cglib 的 jar 文件,但是 Spring 的核心包中已经包括了 Cglib 功能,所 以直接引入 spring-core-xxx.jar 即可.
- 引入功能包后,就可以在内存中动态构建子类
- 代理的类不能为 final,否则报错
- 目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额 外的业务方法
CGLIB 创建的动态代理对象比JDK 创建的动态代理对象的性能更高,但是 CGLIB 创建代理对象时所花费的时间却比 JDK 多得多。所以对于单例的对象,因为无 需频繁创建对象,用 CGLIB 合适,反之使用 JDK 方式要更为合适一些。同时由 于 CGLib 由于是采用动态创建子类的方法,对于 final 修饰的方法无法进行代理。