在软件开发中,我们常常会遇到这样的问题:现有的代码无法直接使用在新的应用场景中。这时,重写代码代价太大,因此使用适配器模式(Adapter Pattern)可以很好地解决这个问题。适配器模式是一种结构型设计模式,它允许将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
#### 不使用适配器的代码实现
首先,我们来看一下如果不使用适配器模式,如何实现一个简单的多媒体播放器。我们将实现一个能够播放mp3、mp4和avi文件的播放器。
**MediaPlayer.h**
```c
#ifndef MEDIA_PLAYER_H
#define MEDIA_PLAYER_H
void playMp3(const char* fileName);
void playMp4(const char* fileName);
void playAvi(const char* fileName);
#endif // MEDIA_PLAYER_H
```
**MediaPlayer.c**
```c
#include "MediaPlayer.h"
#include <stdio.h>
#include <string.h>
void playMp3(const char* fileName)
{
printf("Playing mp3 file: %s\n", fileName);
}
void playMp4(const char* fileName)
{
printf("Playing mp4 file: %s\n", fileName);
}
void playAvi(const char* fileName)
{
printf("Playing avi file: %s\n", fileName);
}
```
**Main.c**
```c
#include "MediaPlayer.h"
int main()
{
playMp3("song.mp3");
playMp4("video.mp4");
playAvi("movie.avi");
return 0;
}
```
在上面的实现中,我们直接在`MediaPlayer`中定义了播放不同格式文件的函数。这种设计非常简单,但缺乏灵活性。如果我们需要增加新的文件格式,就必须修改`MediaPlayer`,违反了**开放/封闭原则(OCP)**。
#### 使用适配器模式的代码实现
现在,我们来看一下如何使用适配器模式改进上述代码,以实现更好的灵活性和可扩展性。
**AdvanceMediaPlayer.h**
```c
#ifndef ADVANCE_MEDIA_PLAYER_H
#define ADVANCE_MEDIA_PLAYER_H
typedef struct
{
void (*playMp4)(const char* fileName);
void (*playAvi)(const char* fileName);
} AdvanceMediaPlayer;
#endif // ADVANCE_MEDIA_PLAYER_H
```
**AviPlayer.h**
```c
#ifndef AVI_PLAYER_H
#define AVI_PLAYER_H
#include "AdvanceMediaPlayer.h"
typedef struct
{
AdvanceMediaPlayer base;
} AviPlayer;
AviPlayer* AviPlayerNew();
void AviPlayerPlayMp4(const char* fileName);
void AviPlayerPlayAvi(const char* fileName);
#endif // AVI_PLAYER_H
```
**AviPlayer.c**
```c
#include "AviPlayer.h"
#include <stdio.h>
#include <stdlib.h>
void AviPlayerPlayMp4(const char* fileName)
{
// Do nothing
}
void AviPlayerPlayAvi(const char* fileName)
{
printf("Playing avi file: %s\n", fileName);
}
AviPlayer* AviPlayerNew()
{
AviPlayer* player = (AviPlayer*)malloc(sizeof(AviPlayer));
player->base.playMp4 = AviPlayerPlayMp4;
player->base.playAvi = AviPlayerPlayAvi;
return player;
}
```
**Mp4Player.h**
```c
#ifndef MP4_PLAYER_H
#define MP4_PLAYER_H
#include "AdvanceMediaPlayer.h"
typedef struct
{
AdvanceMediaPlayer base;
} Mp4Player;
Mp4Player* Mp4PlayerNew();
void Mp4PlayerPlayMp4(const char* fileName);
void Mp4PlayerPlayAvi(const char* fileName);
#endif // MP4_PLAYER_H
```
**Mp4Player.c**
```c
#include "Mp4Player.h"
#include <stdio.h>
#include <stdlib.h>
void Mp4PlayerPlayMp4(const char* fileName)
{
printf("Playing mp4 file: %s\n", fileName);
}
void Mp4PlayerPlayAvi(const char* fileName)
{
// Do nothing
}
Mp4Player* Mp4PlayerNew()
{
Mp4Player* player = (Mp4Player*)malloc(sizeof(Mp4Player));
player->base.playMp4 = Mp4PlayerPlayMp4;
player->base.playAvi = Mp4PlayerPlayAvi;
return player;
}
```
**MediaPlayer.h**
```c
#ifndef MEDIA_PLAYER_H
#define MEDIA_PLAYER_H
typedef struct
{
void (*play)(const char* type, const char* fileName);
} MediaPlayer;
#endif // MEDIA_PLAYER_H
```
**ClassicMediaPlayer.h**
```c
#ifndef CLASSIC_MEDIA_PLAYER_H
#define CLASSIC_MEDIA_PLAYER_H
#include "MediaPlayer.h"
typedef struct
{
MediaPlayer base;
} ClassicMediaPlayer;
ClassicMediaPlayer* ClassicMediaPlayerNew();
void ClassicMediaPlayerPlay(const char* type, const char* fileName);
#endif // CLASSIC_MEDIA_PLAYER_H
```
**ClassicMediaPlayer.c**
```c
#include "ClassicMediaPlayer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void ClassicMediaPlayerPlay(const char* type, const char* fileName)
{
if (strcasecmp(type, "mp3") == 0)
{
printf("Playing mp3 file: %s\n", fileName);
}
else
{
printf("Not supported format: %s\n", type);
}
}
ClassicMediaPlayer* ClassicMediaPlayerNew()
{
ClassicMediaPlayer* player = (ClassicMediaPlayer*)malloc(sizeof(ClassicMediaPlayer));
player->base.play = ClassicMediaPlayerPlay;
return player;
}
```
**PlayerAdapter.h**
```c
#ifndef PLAYER_ADAPTER_H
#define PLAYER_ADAPTER_H
#include "MediaPlayer.h"
#include "AdvanceMediaPlayer.h"
typedef struct
{
MediaPlayer base;
AdvanceMediaPlayer* advanceMediaPlayer;
} PlayerAdapter;
PlayerAdapter* PlayerAdapterNew(const char* type);
void PlayerAdapterPlay(const char* type, const char* fileName);
#endif // PLAYER_ADAPTER_H
```
**PlayerAdapter.c**
```c
#include "PlayerAdapter.h"
#include "Mp4Player.h"
#include "AviPlayer.h"
#include "ClassicMediaPlayer.h"
#include <stdlib.h>
#include <string.h>
void PlayerAdapterPlay(const char* type, const char* fileName)
{
PlayerAdapter* adapter = (PlayerAdapter*)this;
if (strcasecmp(type, "mp4") == 0)
{
adapter->advanceMediaPlayer->playMp4(fileName);
}
else if (strcasecmp(type, "avi") == 0)
{
adapter->advanceMediaPlayer->playAvi(fileName);
}
else
{
ClassicMediaPlayer* classicPlayer = ClassicMediaPlayerNew();
classicPlayer->base.play(type, fileName);
free(classicPlayer);
}
}
PlayerAdapter* PlayerAdapterNew(const char* type)
{
PlayerAdapter* adapter = (PlayerAdapter*)malloc(sizeof(PlayerAdapter));
if (strcasecmp(type, "mp4") == 0)
{
adapter->advanceMediaPlayer = (AdvanceMediaPlayer*)Mp4PlayerNew();
}
else if (strcasecmp(type, "avi") == 0)
{
adapter->advanceMediaPlayer = (AdvanceMediaPlayer*)AviPlayerNew();
}
adapter->base.play = PlayerAdapterPlay;
return adapter;
}
```
**ExtendMediaPlayer.h**
```c
#ifndef EXTEND_MEDIA_PLAYER_H
#define EXTEND_MEDIA_PLAYER_H
#include "MediaPlayer.h"
typedef struct
{
MediaPlayer base;
} ExtendMediaPlayer;
ExtendMediaPlayer* ExtendMediaPlayerNew();
void ExtendMediaPlayerPlay(const char* type, const char* fileName);
#endif // EXTEND_MEDIA_PLAYER_H
```
**ExtendMediaPlayer.c**
```c
#include "ExtendMediaPlayer.h"
#include "PlayerAdapter.h"
#include <stdlib.h>
void ExtendMediaPlayerPlay(const char* type, const char* fileName)
{
PlayerAdapter* adapter = PlayerAdapterNew(type);
adapter->base.play(type, fileName);
free(adapter);
}
ExtendMediaPlayer* ExtendMediaPlayerNew()
{
ExtendMediaPlayer* player = (ExtendMediaPlayer*)malloc(sizeof(ExtendMediaPlayer));
player->base.play = ExtendMediaPlayerPlay;
return player;
}
```
**Main.c**
```c
#include "ExtendMediaPlayer.h"
int main()
{
MediaPlayer* audioPlayer = (MediaPlayer*)ExtendMediaPlayerNew();
audioPlayer->play("mp3", "beyond the horizon.mp3");
audioPlayer->play("mp4", "alone.mp4");
audioPlayer->play("avi", "far far away.avi");
audioPlayer->play("vlc", "mind me.vlc");
free(audioPlayer);
return 0;
}
```
#### 使用适配器和不使用适配器的设计模式原则对比
**不使用适配器的实现**:
- **单一职责原则(SRP)**:实现了不同格式的播放功能在同一个文件中,这个文件的职责不单一。
- **开放/封闭原则(OCP)**:每当我们需要支持新的文件格式时,我们都需要修改现有的代码,违反了开放/封闭原则。
- **依赖倒置原则(
DIP)**:高级模块直接依赖于低级模块,没有抽象层。
**使用适配器模式的实现**:
- **单一职责原则(SRP)**:每个类都有单一的职责,如`Mp4Player`负责播放mp4,`AviPlayer`负责播放avi等。
- **开放/封闭原则(OCP)**:系统在扩展时可以通过增加新的播放器类来支持新的格式,而不需要修改现有的代码。
- **依赖倒置原则(DIP)**:高级模块依赖于抽象,`ExtendMediaPlayer`依赖于`MediaPlayer`接口和`AdvanceMediaPlayer`接口,而不是具体的实现。
通过适配器模式,我们不仅遵循了设计原则,还使得代码更易于扩展和维护。这种设计模式使得我们可以轻松地增加新的文件格式支持而不需要修改现有的代码,从而提高了代码的灵活性和可维护性。