装饰器模式、代理模式和适配器模式都是结构型设计模式,它们的主要目标都是将将类或对象按某种布局组成更大的结构,使得程序结构更加清晰。这里将装饰器模式、代理模式和适配器模式进行比较,主要是因为三个设计模式的类图结构相似度较高、且功能上存在一定的相似度。接下来将一步步说明。
装饰器模式
装饰器模式是一种结构型设计模式,用来动态地给一个对象增加一些额外的职责,即增加该对象额外的功能,其别名为包装器(Wrapper)。在装饰器模式中,会定义一个装饰器类,声明了一个指向被封装对象的引用成员变量,并定义了可动态添加到被封装对象的具体行为。其类图表示如下:
从类图结构上来说,装饰器模式并没有直接修改对象,而是在不影响对象的情况下,以动态、透明的方式给单个对象添加职责。就增加对象功能来说,装饰器模式比生成子类实现更为灵活。
装饰器模式主要适用于需要给对象动态地增加一些额外职责的场景。装饰器能将业务逻辑组织为层次结构,开发者可为各层创建一个装饰器,在运行时将各种不同逻辑组合成对象。特别是当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时,可以考虑使用装饰器模式。不能采用继承的情况主要有三类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类定义不能继承(如final类);第三类是需要扩展的类来源于三方件,对于三方件的代码,如无特殊说明,是不建议使用继承来扩展功能的。
由于被装饰对象和装饰对象可以独立变化,用户可以根据需要增加新的被装饰类和装饰类,原有代码无须改变,这符合"开闭原则"。相比通过继承关系扩展对象的功能,装饰器模式可以提供比继承更多的灵活性,可以在不创建新子类的前提下,扩展对象的行为。但是,装饰器也带来代码复杂度上升的问题。使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。此外,装饰器模式比继承更加灵活,也同时意味着装饰器模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。
代理模式
代理模式是一种结构型设计模式,用来为一个对象提供一种代理以控制对该对象的访问,即通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。代理对象控制着对于原对象的访问,并允许在将请求提交给原对象前后进行一些处理。为使用代理对象控制对原对象的访问,需要创建一个代理类并封装对原对象的访问。其类图表示如下:
代理模式适用于各种需要代理的场景,如远程代理、虚拟代理、缓存代理、动态代理等等。以动态代理为例,对于基于Spring框架开发的应用来说,Spring提供的AOP功能,可以看成是动态代理的一种应用。AOP通过切面的设计,可以在不影响对象的前提下,动态的增加代理完成对象的功能增加。
代理模式,在访问对象时引入了一定程度的间接性。因为可以在不对原对做出修改的情况下创建新代理,所以代理模式符合开闭原则。但代理模式的设计,也带来了代码复杂度上升的问题。实现代理模式需要额外的工作,有些代理模式的实现非常复杂(如动态代理)。此外,由于在客户端和真实对象之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,从而带来服务响应的延迟。但是,这部分影响是极小的。
从类图结构上来说,装饰器模式和代理模式的相似度极高,但是两者还是有一定的差异。如果说装饰器模式的重心是动态地给对象增加一些职责模式,那么代理模式的重心则是通过代理对象间接地访问实际对象,并在代理过程中添加一些额外的逻辑或控制。代理模式主要应用于对象访问困难或复杂、或无法直接访问等场景。简言之,装饰器模式主要关注增强对象的功能,而代理模式则更多关注对对象访问的控制,并不直接增强对象的功能。
适配器模式
适配器模式是一种结构型设计模式,用于将一个类的接口转换成用户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器。根据适配器是根据继承方式,还是组合方式和待适配的类进行交互,可以将
适配器模式分为对象适配器和类适配器两种实现。对象适配器类图表示如下:
类适配器类图表示如下:
从类图结构上可以看到,对象适配器和类适配器的唯一差别就是适配器类(Adapter)和待适配类(Adaptee)的关系。对于类适配器来说,适配器类作为待适配类的子类,对原有方法进行了重写。而对于对象适配器来说,适配器类将待适配器类作为其成员,通过调用待适配器类的接口,完成了原有方法的重写。在实际的应用中,对于需要使用适配器的场景,应优先选用对象适配器。只有在对象适配器不合适的场景下,才考虑使用类适配器。如需要复用这样一些类,他们处于同一个继承体系,并且他们又有了额外的一些共同的方法,这时类适配器就比对象适配器更适合。
适配器模式主要适用于需要使用某个现有类,但是这个现有类的接口不符合系统的需要,或想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类一起工作等场景。适配器模式通过创建一个中间层类,可将其作为代码与遗留类、第三方类或提供接口的类之间的转换器。
适配器可以让接口不兼容的对象相互合作。通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码,实现了目标类和适配者类的解耦。且可以将接口或数据转换代码从目标类主要业务逻辑中分离出来,符合单一职责原则。但是,适配器模式也带来了代码整体复杂度上升的问题,开发者需要新增一系列接口和类。
适配器模式和装饰器模式都被称为包装器,但是两者适用的场合不同。适配器模式的主要目的是将一个类的接口转换成客户端所期望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起协作。装饰器模式则是强调给对象增加一些新的行为或责任。适配器模式重点关注接口转换和兼容性,而装饰器模式则重点关注为对象动态地添加新的行为或责任。
装饰器模式、代理模式和适配器模式对比
无论装饰模式、代理模式和适配器模式,都是结构型设计模式。其主要职责都是将将类或对象按某种布局组成更大的结构,使得程序结构更加清晰。从类图结构上来说,装饰器模式和代理模式和对象适配器的结构上差异不大,都是通过一个外部类去组合目标类,并封装其行为。从功能上来说,装饰器模式关注的是给对象增加一些新的行为或责任。相比基于继承关系扩展对象的功能,装饰器模式可以提供比继承更多的灵活性,可以在不创建新子类的前提下,扩展对象的行为。代理模式更多的关注对象访问的控制,并不直接增强对象的功能。通过代理对象间接地访问实际对象,并在代理过程中添加一些额外的逻辑或控制。而适配器模式则关注接口转换或接口兼容性。通过将一个类的接口转换成客户端所期望的另一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的类可以一起协作。
在实际的应用中,针对装饰器模式、代理模式和适配器模式的选用上,还是要从功能上出发,因为三个设计模式在类图结构上差异并不大。通过其应用场景选择合适的设计模式,如对于对象功能增强的场景,选用装饰器模式,对于对象访问控制的场景,选用代理模式,对于接口的转换或兼容的场景,选用适配器模式。
参考
https://yiyan.baidu.com/ 文心一言
https://blog.csdn.net/kingmax54212008/article/details/107294704 OOAD-设计模式(四)结构型模式之适配器、装饰器、代理模式