设计模式6:适配器模式

适配器模式是一种结构型设计模式,它允许你将不兼容的对象包装到一个适配器中,使其与其他类兼容。

**意图:**将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

**主要解决:**主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。

何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)

**如何解决:**继承或依赖(推荐)。

**关键代码:**适配器继承或依赖已有的对象,实现想要的目标接口。

应用实例: 1、美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。 2、JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。 3、在 LINUX 上运行 WINDOWS 程序。 4、JAVA 中的 jdbc。

优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。

缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

**使用场景:**有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。

**注意事项:**适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。

示例

让我们通过一个简单的例子来解释适配器模式。假设你有一个MediaPlayer接口,它有一个play方法,可以播放mp3文件。现在你想要扩展你的媒体播放器,使其能够播放mp4和vlc文件。但是,你不想修改现有的MediaPlayer接口,因为它可能被大量的现有代码使用。这时,你就可以使用适配器模式。

首先,我们定义一个MediaPlayer接口和一个MP3Player实现:

type MediaPlayer interface {
    Play(audioType string, filename string)
}

type MP3Player struct {
}

func (mp3 *MP3Player) Play(audioType string, filename string) {
    if audioType == "mp3" {
        fmt.Printf("Playing mp3 file. Name: %s\n", filename)
    } else {
        fmt.Printf("Invalid media. %s format not supported\n", audioType)
    }
}

然后,我们定义一个MediaAdapter,它实现了MediaPlayer接口,并且可以适配不同的音频类型:

type MediaAdapter struct {
    advancedMusicPlayer AdvancedMediaPlayer
}

func (ma *MediaAdapter) MediaAdapter(audioType string) {
    if audioType == "vlc" {
        ma.advancedMusicPlayer = &VLCPlayer{}
    } else if audioType == "mp4" {
        ma.advancedMusicPlayer = &MP4Player{}
    }
}

func (ma *MediaAdapter) Play(audioType string, filename string) {
    if audioType == "vlc" {
        ma.advancedMusicPlayer.PlayVLC(filename)
    } else if audioType == "mp4" {
        ma.advancedMusicPlayer.PlayMP4(filename)
    }
}

最后,我们修改MP3Player,使其可以使用MediaAdapter播放其他类型的音频:

func (mp3 *MP3Player) Play(audioType string, filename string) {
    //播放 mp3 音乐文件的内置支持
    if audioType == "mp3" {
        fmt.Printf("Playing mp3 file. Name: %s\n", filename)
    }
    //mediaAdapter 提供了播放其他文件格式的支持
    else if audioType == "vlc" || audioType == "mp4" {
        mediaAdapter := &MediaAdapter{}
        mediaAdapter.MediaAdapter(audioType)
        mediaAdapter.Play(audioType, filename)
    } else {
        fmt.Printf("Invalid media. %s format not supported\n", audioType)
    }
}

通过这种方式,我们就可以在不修改MediaPlayer接口的情况下,使其可以播放其他类型的音频。