适配器与装饰器模式

本文介绍了适配器模式和装饰器模式的概念及其在I/O类库中的应用。适配器模式用于转换接口,使两个接口不匹配的类能够协同工作。装饰器模式则是在不改变对象接口的前提下增强其功能。

适配器模式

适配器模式,就是把一个类的接口变换成客户端所能接受的另一个接口,从而使两个接口不匹配的两个类能够在一起工作。
通常用于一个项目需要引用一些开源框架来一起工作的情况下,这些开源框架都有一些关于环境信息的接口,需要从外部传入,但外部接口不一定能匹配,在这种请款下,就需要适配器模式来转换接口。

适配器模式的结构

适配器模式的类结构如图所示:

这里写图片描述

各角色说明如下:

  • Target(目标接口):所需要转换的所期待的接口
  • Adaptee(源角色):需要适配的接口
  • Adapter(适配器):将原接口适配成目标接口,继承原接口,实现目标接口

I/O中的适配器模式

适配器的作用就是将一个接口适配到另一个接口,在I/O类库有很多这样的类子,如将字符串数据转成字节数据保存到文件中,将字节数据转换成流数据等。下面以InputStreamReaderOutputStreamWriter 类为例来介绍适配器模式。
InputStreamReaderOutputStreamWriter分别继承了Reader和Writer接口,但要创建它们的对象必须在构造函数传入InputStream和OutputStream实例。InputStreamReaderOutputStreamWriter的作用也是将InputStream和OutputStream适配到Reader和Writer,InputStreamReader的类结构如图

这里写图片描述

InputStreamReader实现了Reader接口,并持有了InputStream的引用,这里是通过StreamDecoder类间接持有的,因为从byte到char要经过解码。
很明显适配器就是InputStreamReader类,源角色就是InputStream代表的实例对象,目标接口就是Reader类了。
I/O类库中还有很多类似的用法,如StringReader将String类适配到Reader接口,ByteArrayInputStream适配器将byte数组适配到InputStream流处理接口。

装饰器模式

装饰器模式,顾名思义,就是将某个类重新装扮下,使它在功能上更强大,但是作为原来的这个类,使用者不应该感受到装饰前与装饰后有什么不同,否则就破坏了原有类的结构,所以装饰器模式要做到对装饰类的使用者透明。

装饰器模式的结构

这里写图片描述

图中各角色描述如下:

  • Component:抽象组建角色,定义一组抽象的接口,规定这个被装饰组件都有哪些功能。
  • ConcreteComponent:实现这个抽象组件的所有功能;
  • Decorator:装饰器角色,它持有一个Component对象实例的引用,定义一个与抽象组件一致的接口;
  • ConcreteDecorator:具体的装饰器实践者,负责实现装饰器角色定义的功能。

I/O中的装饰器模式

以FileInputStream为例介绍装饰器模式的使用。

这里写图片描述

如图所示FileInputStream的类结构图,InputStream类是以抽象组件存在的,而FileInputStream是具体组件,它实现了抽象组件的所有接口,FileInputStream无疑就是装饰角色,它实现了InputStream类的所有接口,并持有InputStream的对象的实例引用;BufferedInputStream是具体的装饰器实现者,它给InputStream附加了功能,这个装饰器类的作用使得InputStream读取的数据保存在内存中,从而提高读取的性能。与这个装饰器类类似的功能还有LineNumberInputStream类,它的作用是提高按行读取数据的功能,它们都让InputStream增强了功能或提升了性能。

适配器模式与装饰器模式的区别

装饰器与适配器模式都有一个别名叫包装模式(Wrapper),它们的作用看似都是起到包装一个类或对象的作用,但是使用它们的目的不一样。适配器模式的意义是将一个接口转变成另一个接口,通过改变接口达到重复使用的目的;而装饰器模式不是要改变被装饰对象的接口,而恰恰要保持原有的接口,但增强原有对象的功能,或者改变原有对象的处理方法而提高性能。所有这两个模式的设计目的是不同的。

### 适配器模式装饰器模式的概念及用法 #### 一、适配器模式 适配器模式是一种结构型设计模式,其主要目的是让两个原本不兼容的类能够一起工作。它通过引入一个中间层——适配器,将一个已有的接口转换为目标接口的形式,从而使两者可以协同操作。 适配器模式的核心在于解决现有组件需求之间的差异问题。例如,在某些情况下,我们可能有一个现有的类(即适配者),但它并不完全满足当前系统的需求。此时可以通过创建一个新的适配器类来调整该类的行为或属性,使其适应新的环境[^5]。 ##### 实现方式 适配器模式有两类常见的实现方法: 1. **对象适配器**:利用组合关系完成对接口的封装。这种方式更灵活,因为它不需要改变原有类的结构即可实现功能扩展。 2. **类适配器**:基于继承机制构建适配逻辑。尽管这种方法简单直接,但由于涉及多继承等问题,在现代面向对象语言中较少被推荐使用[^4]。 以下是采用 Python 编写的对象适配器示例: ```python class MediaPlayer: def play(self, audio_type, file_name): pass class AdvancedMediaPlayer: def play_vlc(self, file_name): pass def play_mp4(self, file_name): pass class VlcPlayer(AdvancedMediaPlayer): def play_vlc(self, file_name): print(f"Playing VLC File {file_name}") class Mp4Player(AdvancedMediaPlayer): def play_mp4(self, file_name): print(f"Playing MP4 File {file_name}") class MediaAdapter(MediaPlayer): def __init__(self, audio_type): self.advanced_music_player = None if audio_type.lower() == "vlc": self.advanced_music_player = VlcPlayer() elif audio_type.lower() == "mp4": self.advanced_music_player = Mp4Player() def play(self, audio_type, file_name): if audio_type.lower() == "vlc": self.advanced_music_player.play_vlc(file_name) elif audio_type.lower() == "mp4": self.advanced_music_player.play_mp4(file_name) player = MediaAdapter("vlc") player.play("vlc", "song.vlc") # 输出 Playing VLC File song.vlc ``` 上述代码展示了如何借助适配器模式连接不同的媒体播放器类型[^4]。 --- #### 二、装饰器模式 装饰器模式同样属于一种结构型设计模式,旨在动态地向某个对象添加额外职责而不影响其他对象。这种灵活性允许开发者在运行期间自由决定哪些行为应该增强或者替换掉原有的默认动作。 相比起子类化技术来说,装饰器提供了一种更为轻量级的选择方案;而且由于它是围绕着单个实例展开工作的缘故,因此不会像多重继承那样带来复杂性混乱局面的发生风险[^1]。 ##### 应用场景 当面临如下情况时考虑运用此模式: - 需要增加由一些基本责任组成的附加责任; - 不想使用大量子类去表达仅仅几种状态的变化; - 希望保持各层次之间相互独立的关系以便日后维护升级变得容易些[^3]。 下面是一个简单的 Java 版本的例子展示怎样给咖啡加调料的过程模拟出来: ```java abstract class Beverage { String description = "Unknown Beverage"; public String getDescription(){ return this.description; } abstract double cost(); } // Concrete Component class Espresso extends Beverage{ public Espresso(){ this.description="Espresso Coffee"; } @Override public double cost() { System.out.println("Cost of "+this.getDescription()+" : $1"); return 1d; } } interface CondimentDecorator extends Beverage{} // Decorator AKA Wrapper Class class Mocha implements CondimentDecorator { private final Beverage beverage; public Mocha(Beverage b){ this.beverage=b; } @Override public String getDescription(){ return this.beverage.getDescription()+ ", Mocha"; } @Override public double cost(){ return .20 + this.beverage.cost(); } } Beverage drink=new Mocha(new Espresso()); System.out.println(drink.getDescription()); // Output: Espresso Coffee, Mocha System.out.println("$"+drink.cost()); // Output: Cost of Espresso Coffee : $1 , Total Cost:$1.2 ``` 这段脚本清楚表明了即使是在最基础的产品之上也可以轻松叠加多种定制选项[^1]。 --- ### 总结 虽然二者都归属于结构性设计范畴之内,但是它们各自侧重点完全不同。前者注重的是桥接不同体系间的鸿沟,后者则是为了赋予已有实体更多的可能性。所以在实际项目规划过程中应当依据具体情况合理选用相应策略以达到最佳效果[^2]。 问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值