1. 适配器模式介绍
1.1 适配器模式介绍
适配器模式(adapter pattern)的原始定义是:将一个类的接口转换为客户期望的另一个接口,适配器可以让不兼容的两个类一起协同工作。
适配器模式的主要作用是把原本不兼容的接口,通过适配修改做到统一,使得用户方便使用。比如,万能充电器和多接口数据线都是为了适配各种不同的接口。
为什么要转换接口?
- 原接口和目标接口都已经存在,不易修改接口代码。
- 抽象接口希望复用已有组件的逻辑。
1.2 适配器模式结构
适配器模式(Adapter)包含以下主要角色:
- 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
- 适配者(Adaptee)类:被适配的角色,它是被访问和适配的现存组件库中的组件接口。
- 适配器(Adapter)类:一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
适配器模式分为:
-
类适配器
-
对象适配器
两者的区别在于:适配器与适配者的关系。类适配器是继承关系,对象适配器是聚合关系。根据设计原则,聚合优先于继承,应多选用对象适配器。
1.3 代码示例
// 目标接口
public interface Target {
void request();
}
// 适配者类
public class Adaptee {
public void specificRequest() {
System.out.println("适配者中的业务代码被调用!");
}
}
// 类适配器
public class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
this.specificRequest();
}
}
// 对象适配器
public class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
this.adaptee.specificRequest();
}
}
// 测试代码
public class Client {
public static void main(String[] args) {
Target classAdapter = new ClassAdapter();
classAdapter.request();
Target objectAdapter = new ObjectAdapter(new Adaptee());
objectAdapter.request();
}
}
2. 适配器模式在实际开发中的应用
2.1 需求描述
为了提升系统的速度,将一些数据以 K-V 形式缓存在内存中,平台提供 get、put、remove 等 API 以及相关的管理机制。
功能实现的迭代过程,从 HashMap 到 Memcached 再到 Redis,要确保后面再增加新的缓存组件时,能够实现自由的切换,并且还要符合开闭原则。
设计问题:
- 如何在符合开闭原则前提下,实现功能的扩展?
- 两种客户端 API 不相同,如何保证自由切换?
使用适配器模式。
2.2 功能实现
使用适配器模式将功能相似的多种第三方组件(实现方案),统一成自己需要的 API,业务代码只依赖已经统一的 API,而不依赖第三方 API。
(1) 定义一个缓存接口,包含 get、put、remove 等操作方法。例如:
public interface Cache {
void put(String key, Object value);
Object get(String key);
void remove(String key);
}
(2) 实现该接口的三个适配器,分别对应 HashMap、Memcached、Redis 三种缓存方案。例如:
// HashMap 适配器
public class HashMapCacheAdapter implements Cache {
private Map<String, Object> cache = new HashMap<>();
@Override
public void put(String key, Object value) {
cache.put(key, value);
}
@Override
public Object get(String key) {
return cache.get(key);
}
@Override
public void remove(String key) {
cache.remove(key);
}
}
// Memcached 适配器
public class MemcachedCacheAdapter implements Cache {
private MemcachedClient memcachedClient;
public MemcachedCacheAdapter(MemcachedClient memcachedClient) {
this.memcachedClient = memcachedClient;
}
@Override
public void put(String key, Object value) {
memcachedClient.set(key, 0, value);
}
@Override
public Object get(String key) {
return memcachedClient.get(key);
}
@Override
public void remove(String key) {
memcachedClient.delete(key);
}
}
// Redis 适配器
public class RedisCacheAdapter implements Cache {
private Jedis jedis;
public RedisCacheAdapter(Jedis jedis) {
this.jedis = jedis;
}
@Override
public void put(String key, Object value) {
jedis.set(key, value.toString());
}
@Override
public Object get(String key) {
return jedis.get(key);
}
@Override
public void remove(String key) {
jedis.del(key);
}
}
(3) 创建工厂类,根据配置文件中的配置来创建相应的缓存适配器。例如:
public class CacheAdapterFactory {
public static Cache createCacheAdapter(String type) {
if ("HashMap".equals(type)) {
return new HashMapCacheAdapter();
} else if ("Memcached".equals(type)) {
MemCachedClient memCachedClient = new MemCachedClient();
return new MemcachedCacheAdapter(memCachedClient);
} else if ("Redis".equals(type)) {
Jedis jedis = new Jedis("localhost", 6379);
return new RedisCacheAdapter(jedis);
} else {
throw new IllegalArgumentException("Invalid cache type: " + type);
}
}
}
使用时,只需要调用工厂类的 createCacheAdapter
方法,传入缓存类型即可获取相应的缓存适配器。例如:
public class Client {
public static void main(String[] args) {
Cache cache = CacheAdapterFactory.createCacheAdapter("Redis");
cache.put("key", "value");
Object result = cache.get("key");
cache.remove("key");
}
}
3. 适配器模式总结
优点:
- 解耦合: 适配器模式允许两个没有直接关联的类协同工作,降低了它们之间的耦合度。
- 提高复用性: 通过适配器,可以重用现有的类,而不需要修改它们的代码。
- 统一接口: 适配器模式提供了一种方法来统一多个不同的接口,使得它们可以被统一对待。
- 隐藏实现: 适配器模式隐藏了现有类的实现细节,只暴露出需要的接口。
- 灵活性: 可以根据需要自由地适配不同的类,提供了高度的灵活性。
缺点:
- 单一适配限制: 使用类适配器时,一次最多只能适配一个适配者类,这可能限制了其应用范围。
- 系统复杂度: 如果过度使用适配器,可能会导致系统结构变得复杂,难以理解和维护。
适用场景:
- 接口统一: 当需要统一多个类的接口时,适配器模式可以有效地将它们适配到一个统一的接口。
- 兼容性需求: 当现有的接口无法修改,但需要与其他系统或模块兼容时,适配器模式可以提供解决方案。