意图
把一个类的接口变换成客户端所期待的另一种接口从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作
场景
假设网络游戏的客户端程序分两部分一部分是和服务端通讯的大厅部分大厅部分提供的功能有道具购买读取房间列表创建房间以及启动游戏程 序另一部分就是游戏程序了游戏程序和大厅程序虽然属于一个客户端但是由不同的公司在进行开发游戏大厅通过实现约定的接口和游戏程序进行通讯
一开始的设计就是大厅程序是基于接口方式调用游戏程序启动游戏场景方法的在大厅程序开发接近完成的时候公司决定和另外一家游戏公司合作 因此希望把大厅程序能适用另一个游戏而这个新游戏的遵循的是另一套接口是不是可以避免修改原先调用方法来启动场景呢?或许你会说既然只有一个方法修 改那么修改一下也无妨我们假设大厅程序和游戏程序之间有个接口其中的大部分都有修改呢?因为游戏程序接口的修改大厅程序可能要修改不止 个地方这样接口的意义何在呢?
此时可以考虑使用Adapter模式来适配这种接口的不匹配情况
using System;
using SystemCollectionsGeneric;
using SystemText;
namespace AdapterExample
{
class Program
{
static void Main(string[] args)
{
Lobby lobby = new Lobby();
lobbyCreateRoom(HalfPaper);
lobbyStartGame();
}
}
interface IGame
{
void StartScene(string sceneName);
void EnterPlayer(string playerName);
}
class Lobby
{
private string sceneName;
public void CreateRoom(string sceneName)
{
thissceneName = sceneName;
}
public void StartGame()
{
IGame game = new GameAdapter();
gameStartScene(sceneName);
gameEnterPlayer(yzhu);
}
}
class Game
{
public void LoadScene(string sceneName string token)
{
if (token == Abcd)
ConsoleWriteLine(Loading + sceneName + );
else
ConsoleWriteLine(Invalid token!);
}
public void EnterPlayer(int playerID)
{
ConsoleWriteLine(player: + playerID + entered);
}
}
class GameAdapter : IGame
{
private Game game = new Game();
public void StartScene(string sceneName)
{
gameLoadScene(sceneName Abcd);
}
public void EnterPlayer(string playerName)
{
gameEnterPlayer(GetPlayerIDByPlayerName(playerName));
}
private int GetPlayerIDByPlayerName(string playerName)
{
return ;
}
}
}
可以看到原先的接口中启动游戏场景只需要一个参数就是游戏场景名而进入新的玩家需要提供玩家ID(新游戏都使用玩家ID而不使用玩家账户名)
IGame接口就是适配器模式中的目标角色这是客户所期待的接口也是针对老的游戏程序所遵循的接口
Lobby类相当于调用方或者客户它原先的代码可能是如下的
Game game = new Game();
但是由于接口的改变现在不能直接实例化游戏类只能实例化适配器类型虽然还是需要改动但是这个改动是很小的而且完全可以通过用动态加载程序集来消除这种改动
GameAdapter类是适配器角色它是适配器模式的核心用于把源接口转变为目标接口在这里我们看到它实现目标接口
Game类型是源角色或者说是需要适配的对象或许它也遵循了另外一套接口不过我们不是很关心这个因此代码中也没有体现
使用了适配器模式后客户端代码没有做什么修改客户端代码老老实实的依赖接口它并没有错如果因此依赖对象的修改而需要大幅度修改就很无辜 了我们在适配器中把本来没有关联的两个接口适配在了一起我们可以看到适配器做的不仅仅是换一换方法名如果源角色和目标角色的差异非常大那么适配 器需要做很多工作
何时采用
从代码角度来说 如果你希望分离复杂类型构建规则和类型内部组成或者希望把相同的构建过程用于构建不同类型的时候可以考虑使用建造者模式
从应用角度来说 如果你希望解耦产品的创建过程和产品的具体配件或者你希望为所有产品的创建复用一套稳定并且复杂的逻辑的时候可以考虑使用建造者模式
实现要点
适配器模式是否能成功运用的关键在于代码本身是否是基于接口编程的如果不是的话那么适配器无能为力
适配器模式的实现很简单基本的思想就是适配器一定是遵循目标接口的
适配器模式的变化比较多可以通过继承和组合方式进行适配适配器可以是一组适配器产品适配器也可以是抽象类型
适配器模式和Facade的区别是前者是遵循接口的后者可以是不遵循接口的比较灵活
适配器模式和Proxy的区别是前者是为对象提供不同的接口或者为对象提供相同接口并且前者有一点后补的味道后者是在设计时就会运用的
注意事项
在对两个无关类进行适配的时候考虑一下适配的代价一个非常庞大的适配器可能会对系统性能有影响