一、适配器设计模式

1、是什么?

1、适配器模式将某个类的接口转换成客户端期望的另一个接口表示,主要的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作
2、适配器模式属于结构型模式
3、主要分为三类:类适配器模式、对象适配器模式、接口适配器模式

2、工作原理

(1)适配器模式:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容

(2)从用户角度看不到被适配者,是解耦的

(3)用户调用适配器转化出来的目标接方法,适配器再调用被适配器的相关接口方法

(4)用户收到反馈结果,感觉只是和目标关系交互,如图:

【设计模式】23种设计模式之结构型模式_开发语言

3、类适配器模式

例子:给手机充电,插座为220V、手机支持5V

【设计模式】23种设计模式之结构型模式_设计模式_02

4、对象适配器模式

介绍: (1)基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src而是持有src类的实例,以解决兼容性的问题。
(2)根据 “合成复用原则” ,在系统中尽量使用关联关系来替代继承关系。

【设计模式】23种设计模式之结构型模式_开发语言_03

5、接口适配器模式

(1)核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现【空方法】,那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求;
(2)适用于一个接口不想使用其所有的方法的情况。

6、适配器模式在SpringMVC框架应用的源码剖析

(1)SpringMVC中的HandlerAdapter,就使用了适配器模式
(2)SpringMVC处理请求的流程回顾
(3)使用HandlerAdapter的原因分析:

可以看到处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用Controller方法,需要调用的时候就得不断的使用if else来进行判断是哪一种子类然后执行。那么如果后面要扩展Controller,就得修改原来的代码,这样就违背了OCP原则。

SpringMVC具体的流程:

  1. 用户通过浏览器发起 HttpRequest 请求到前端控制器 (DispatcherServlet)。
  2. DispatcherServlet 将用户请求发送给处理器映射器 (HandlerMapping)。
  3. 处理器映射器 (HandlerMapping)会根据请求,找到负责处理该请求的处理器,并将其封装为处理器执行链 返回 (HandlerExecutionChain) 给 DispatcherServlet
  4. DispatcherServlet 会根据 处理器执行链 中的处理器,找到能够执行该处理器的处理器适配器(HandlerAdaptor) --注,处理器适配器有多个
  5. 处理器适配器 (HandlerAdaptoer) 会调用对应的具体的 Controller
  6. Controller 将处理结果及要跳转的视图封装到一个对象 ModelAndView 中并将其返回给处理器适配器 (HandlerAdaptor)
  7. HandlerAdaptor 直接将 ModelAndView 交给 DispatcherServlet ,至此,业务处理完毕
  8. 业务处理完毕后,我们需要将处理结果展示给用户。于是DisptcherServlet 调用 ViewResolver,将 ModelAndView 中的视图名称封装为视图对象
  9. ViewResolver 将封装好的视图 (View) 对象返回给 DIspatcherServlet
  10. DispatcherServlet 调用视图对象,让其自己 (View) 进行渲染(将模型数据填充至视图中),形成响应对象 (HttpResponse)
  11. 前端控制器 (DispatcherServlet) 响应 (HttpResponse) 给浏览器,展示在页面上。

二、桥接模式(Bridge)

1、手机操作问题

现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网、打电话等),如图:

【设计模式】23种设计模式之结构型模式_设计模式_04

2、传统方案解决手机操作问题

【设计模式】23种设计模式之结构型模式_目标对象_05

3、传统方案解决手机操作问题分析

(1)扩展性我net【类爆炸】,如果我们再增加手机的样式,就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
(2)违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌手机,这样增加了代码维护成本。
(3)解决方案-使用桥接模式

4、桥接模式(Bridge)是什么?

(1)桥接模式是指:将实现抽象放在两个不同的类层次中,使两个层次可以独立改变。
(2)是一种结构型设计模式
(3)Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象【Abstraction】行为实现【Implementation】分离开来,从而可以保证各部门的独立性以及应对他们的功能扩展。

5、使用桥接模式解决手机问题

使用桥接模式改进传统方式,让程序具有更好的扩展性,利用程序维护,类图如下:

【设计模式】23种设计模式之结构型模式_java_06


上代码:

【设计模式】23种设计模式之结构型模式_java_07

6、桥接模式的注意事项

(1)实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
(2)对于系统的高层设计,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。
(3)桥接模式替代继承方案,可以减少子类的个数,降低系统的管理和维护成本。
(4)桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程。
(5)桥接模式要求正确识别出系统中两个独立变化的维度【抽象和实现】,因此其使用范围有一定的局限性,即需要有这样的应用场景。
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

三、装饰者设计模式

1、定义

装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则。

2、用装饰者模式设计的方案

【设计模式】23种设计模式之结构型模式_目标对象_08

3、代码解析

【设计模式】23种设计模式之结构型模式_享元模式_09

四、组合模式

1、定义

1)、组合模式又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构表示“整体-部分”的层次关系;
2)、组合模式依据树形结构来组合对象,用来表示部分以及整体层次;
3)、组合模式使得用户对单一对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象。

2、组合模式原理类图

【设计模式】23种设计模式之结构型模式_java_10

3、组合模式解决的问题

1)、组合模式解决这样的问题:当我们要处理的对象可以生成一颗树形结构,而我们要对树上的节点和叶子进行操作时,它能够提供一致的方式,而不用考虑它是节点还是叶子。

2)、例如:

【设计模式】23种设计模式之结构型模式_享元模式_11

4、组合模式解决学校院系展示

编写程序展示一个学校院系结构:要在一个页面中展示出学校的院系组成,一个学院有多个学院,一个学院有多个系

代码实现:

【设计模式】23种设计模式之结构型模式_设计模式_12

5、组合模式的注意事项和细节

1)、简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题;
2)、具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做除任何改动。
3)、方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的树形结构。
4)、需要遍历组织机构,或者处理的镀锡具有树形结构时,非常适合使用组合模式。
5)、要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式。

五、外观模式

1、需求:组建一个家庭影院

【设计模式】23种设计模式之结构型模式_设计模式_13

2、传统方式如何解决影院管理

【设计模式】23种设计模式之结构型模式_java_14

1)、在ClientTest的main方法中,创建各个子系统的对象,并直接去调用子系统相关方法,会造成调用过程混乱,没有清晰的过程。
2、不利于在ClientTest中去维护子系统的过程。
3、解决思路:定义一个高层接口,给子系统中的一组接口提供一个一致的界面(比如在高层接口提供四个方法ready、play、pause、end),用来访问子系统中的一群接口。
4、也就是说通过定义一个接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节===>外观模式

3、外观模式基本介绍

1)、外观模式Facade,又叫"过程模式":外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这个子系统更加容易使用
2)、外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节。

4、外观模式原理类图

【设计模式】23种设计模式之结构型模式_开发语言_15

5、代码实现

【设计模式】23种设计模式之结构型模式_享元模式_16

6、外观模式的注意事项和细节

1)、外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性;
2)、外观模式对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展;
3)、通过合理的使用外观模式,可以帮我们更好的划分访问的层次;
4)、当系统需要进行分层设计时,可以考虑使用Facade模式;
5)、在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时5可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性;
6)、不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好要以让系统有层次,利于维护为目的。

六、享元模式

1、基本介绍

1)、享元模式(Flyweight Pattern) 也叫蝇量模式:运用共享技术有效地支持大量细粒度的对象。
2)、常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个。
3)、享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓存池时,不需要总是创建新对象,可以从缓冲池拿。这样可以降低系统内存,同时提高效率。
4)、享元模式经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式。

2、原理类图

【设计模式】23种设计模式之结构型模式_设计模式_17

3、代码实现

【设计模式】23种设计模式之结构型模式_设计模式_18

4、源码分析

如果Integer.valueOf(x) 在-128到127之间[包含-128和127] 就使用享元m模式返回 否则new新的Integer

// 小结:
        // 1、在valueOf方法中 先判断值是否在IntegerCache中 如果不在就创建新的Integer 否则就从缓存池中返回
        // 2、valueOf就使用到享元模式
        // 3、如果使用valueOf方法得到一个Integer实例 范围在-128到127 执行速度比new快

        Integer x = Integer.valueOf(127);
        Integer z = new Integer(127);
        Integer y = Integer.valueOf(127);
        Integer w = new Integer(127);

        System.out.println(x.equals(z));// equals比较大小 true ==比较引用
        System.out.println(x == z);// false
        System.out.println(x == y);// true[因为在范围内 从缓存池中取 而非new实例]
        System.out.println(w == x);// false
        System.out.println(w == z);// false

        Integer j = new Integer(200);
        Integer i = new Integer(200);
        System.out.println(j == i);// false

5、注意事项

1)、在享元模式这样理解,“享”就表示共享,“元”表示对象
2)、系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式;
3)、用唯一标识码判断,如果在内存中有,则返回这个唯一标识码的对象,用HashMap/HashTable存储
4)、享元模式大大减少了对象的创建,降低了程序内存的占用,提供效率
5)、享元模式提高了系统的复杂度。需要分离出内部状态和外部状态。而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方
6)、使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
7)、享元模式经典的应用场景是需要缓冲池的场景,比如String常量池、数据库连接池。

七、代理模式

1、基本介绍

1)、代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
2)、被代理的对象可以是远程对象创建开销大的对象或需要安全控制的对象 3)、代理模式有不同的形式,主要有三种 静态代理、动态代理(JDK代理、接口代理)和 Cglib代理(可以在内存动态的创建对象,而不需要实现接口,他是属于动态代理的范畴)

2、静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现想通过的接口或者继承相同父类。

【设计模式】23种设计模式之结构型模式_享元模式_19

静态代理优缺点:
1)、优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
2)、缺点:因为代理对象需要与目标对象实现一样的接口,所有会有很多代理类
3)、一旦接口增加方法,目标对象与代理对象都要维护

3、动态代理

1)、代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理;
2)、代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象;
3)、动态代理也叫做:JDK代理、接口代理

【设计模式】23种设计模式之结构型模式_设计模式_20

4、Cglib代理

1)、静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是Cglib代理

2)、Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib代理归属到动态代理。

3)、Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口,它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截

4)、在AOP编程中如何选择代理模式:

1、目标对象需要实现接口,用JDK代理;2、目标对象不需要实现接口,用Cglib代理。

5)、Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。

【设计模式】23种设计模式之结构型模式_享元模式_21

5、代理模式的变体

1)、防火墙代理:内网通过代理穿透防火墙,实现对公网的访问;
2)、缓存代理:当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存;
3)、远程代理:远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息;
4)、同步代理:主要使用在多线程编程中,完成多线程同步工作。

【设计模式】23种设计模式之结构型模式_享元模式_22