结构类模式包括适配器模式、桥接模式、组合模式、装饰模式、门面模式、享元模式和代理模式。
为什么叫结构类模式呢?因为它们都是通过组合类或对象产生更大结构以适应更高层次的逻辑需求,俗称“包一层”。
七种模式
适配器模式
适配器和适配对象使用不同的接口,基本上实现同样的功能。适配器的主要工作是接口转换。
适配器就是通过一个类的接口变换成客户端所期待的另一种接口,可以帮我们解决接口不兼容的问题。
两个成熟的类需要通信,但是接口不同,由于开闭原则,我们不能去修改这两个类的接口,所以就需要一个适配器来完成衔接过程。
适配器是一种“补偿模式”,用来补救设计上的缺陷。如果在设计初期,我们能设计合理,这种模式就没有应用机会了。
桥接模式
桥接模式将抽象部分与它的实现部分分离,是它们都可以独立地变化。它很好的支持了开闭原则和组合锯和复用原则。实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这些多角度分离出来让他们独立变化,减少他们之间的耦合。
组合模式
组合模式将对象组合成树形结构以表示部分-整体的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式适合应用在数据结构呈现树形结构,比如说,公司的组织结构,有分公司,部门,组等。而且单个对象和组合对象的处理逻辑都一样,可以使用递归的方式来进行处理。
装饰器模式
装饰器和装饰对象使用相同的接口,在装饰对象的基础上增加新功能。装饰器的工作是实现这些新功能。
在不影响原有功能的基础上,添加新的代码,增强原始类的功能。
装饰模式保证的是被修饰的对象功能比原始对象丰富(当然,也可以减弱),但不做准入条件判断和准入参数过滤,如是否可以执行类的功能,过滤输入参数是否合规等,这不是装饰模式关心的。
装饰模式没有通过继承原有类来扩展功能,但却达到了一样的目的,而且比继承更加灵活,所以可以说装饰模式是继承关系的一种替代方案。
门面模式
为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。
门面模式主要是为了让接口更易于使用,而适配器模式是为了兼容接口。是设计的理念不同。
外观模式中,客户对各个具体的子系统是不了解的,所以对这些子系统进行了封装,对外只提供了用户所明白的单一而简单的接口,用户直接使用这个接口就可以完成操作,而不用去理睬具体的过程,而且子系统的变化不会影响到用户,这样就做到了信息隐蔽。
如果我们想要调用 A,B,C接口并获取里面的值,这时候我们可以在这三个接口外面套一个 D 接口。直接调用 D 接口获取 A,B,C 接口的值。这样子的设计让我们省了多次请求的时间。
享元模式
共享同一个元素,是一种节省内存的设计模式,类似池技术。
当系统存在大量的重复对象时,我们就可以用享元模式共享相同的元件对象,从而达到了节省内存的目的,避免对象泛滥造成资源浪费。
享元模式主要作用是实现对象的共享,即使用共享池,从而减少内存分配的开销。享元模式通常与工厂模式一起使用,它包含了多个共享的组合对象,因此:享元模式=单例模式+工厂模式+合成模式。
在享元模式中,我们会新增一个类去保存元素的映射池。而这个新增的类就相当于是一个新增的对象,通过组合的形式去节省内存的消耗。享元模式的结构性更多是通过组合的形式体现的。
对于百万在线的游戏,比如五子棋,其中线条,棋子的颜色,房间的背景都是不会变的,这时候就可以使用享元模式,设计出一个享元对象,然后让使用到时候引用即可。
代理模式
代理和代理对象的接口相同,基本上实现同样的功能。代理的工作是帮助吸收一些额外的要求,如节约内存、增加访问控制、突破软件/硬件通信障碍等。
为其他对象提供一种代理,并由代理对象控制对原对象的引用,以间接控制对原对象的访问。
把当前的行为或功能委托给其他对象执行,代理类负责接口限定:是否可以调用真实角色,以及是否对发送到真实角色的消息进行变形处理,它不对被主题角色(也就是被代理类)的功能做任何处理,保证原汁原味的调用。
代理模式使用到极致开发就是AOP,这是各位采用Spring架构开发必然要使用到的技术,它就是使用了代理和反射的技术。
模式对比
代理模式 VS 装饰器模式
对于两个模式,首先要说的是,装饰器模式就是代理模式的一个特殊应用,两者的共同点是都具有相同的接口,不同点则是代理模式着重对代理过程的控制,而装饰模式则是对类的功能进行加强或减弱,它着重类的功能变化,我们举例来说明它们的区别。
代理模式在Java的开发中非常常见,是大家非常熟悉的模式,应用非常广泛,而装饰模式是一个相对少用的模式,在实际应用中接触比较少,但是也有不少框架项目使用了装饰模式,例如在JDK的java.io.*包中就大量使用装饰模式,类似如下的代码:
OutputStream out = new DataOutputStream(new FileOutputStream("test.txt"))
这是装饰模式的一个典型应用,使用DataOutputStream封装了一个FileOutputStream,以方便进行输出流处理。
装饰模式 VS 适配器模式
装饰模式和适配器模式在通用类图上没有太多的相似点,差别比较大,但是它们的功能有相似的地方:都是包装作用,都是通过委托方式实现其功能。不同点是:装饰模式包装的是自己的兄弟类,隶属于同一个家族(相同接口或父类),适配器模式则修饰非血缘关系类,把一个非本家族的对象伪装成本家族的对象,注意是伪装,因此它的本质还是非相同接口的对象。
两个模式的不同点
意图不同
装饰模式的意图是加强对象的功能,它不改变类的行为和属性,而适配器模式关注的则是转化,它的主要意图是两个不同对象之间的转化。
作用对象不同
装饰模式装饰的对象必须是自己的同宗,也就是相同的接口或父类,只要在具有相同的属性和行为的情况下,才能比较行为是增加还是减弱;适配器模式则必须是两个不同的对象,因为它着重于转换,只有两个不同的对象才有转换的必要,如果是相同对象还转换什么?!
场景不同
装饰模式在任何时候都可以使用,只要是想增强类的功能,而适配器模式则是一个补救模式,一般出现在系统成熟或已经构建完毕的项目中,作为一个紧急处理手段采用。
扩展性不同
装饰模式很容易扩展!今天不用这个修饰,好,去掉;明天想再使用,好,加上。这都没有问题。而且装饰类可以继续扩展下去;但是适配器模式就不同了,它在两个不同对象之间架起了一座沟通的桥梁,建立容易,去掉就比较困难了,需要从系统整体考虑是否能够撤销。
享元模式 VS 单例模式
享元是对象级别的:在多个使用到这个对象的地方都只需要使用这一个对象即可满足要求。
单例是类级别的:这个类必须只能实例化出一个对象。
可以这么说:单例是享元的一种特例。设计模式不用拘泥于具体代码, 代码实现可能有 n 多种方式,而单例可以看做是享元的实现方式中的一种,只不过他比享元更加严格的控制了对象的唯一性。