设计模式学习笔记(五、结构型-适配器模式)
目录:
- 什么是适配器模式
- 为什么要有适配器模式
- 如何实现适配器模式
- 适配器模式的使用场景
- 代理、桥接、装饰、适配器区别
什么是适配器模式
从语义上就能很明白的看出适配器模式是什么,它其实就是用来做适配的,把不兼容的接口转成成兼容的接口。
在生活中有一种东西非常贴合适配器模式,就是读卡器,它是用来兼容内存卡和USB接口。
为什么要有适配器模式
把不兼容的接口转成成兼容的接口
如何实现适配器模式
适配器的实现方式有两种:
1、类适配器:通过继承实现
1 public interface IAdapter { 2 3 void func1(); 4 void func2(); 5 void func3(); 6 }
1 public class Adaptee { 2 3 public void f1() { 4 } 5 6 public void f2() { 7 } 8 9 public void func3() { 10 } 11 }
1 public class Adaptor extends Adaptee implements IAdapter { 2 3 @Override 4 public void func1() { 5 super.f1(); 6 } 7 8 @Override 9 public void func2() { 10 // 重新实现fund2的逻辑 11 } 12 13 // 这里fc()不需要实现,直接继承自Adaptee,这是跟对象适配器最大的不同点 14 }
2、对象适配器:通过组合实现
1 public interface IAdapter { 2 3 void func1(); 4 void func2(); 5 void func3(); 6 }
1 public class Adaptee { 2 3 public void f1() { 4 } 5 6 public void f2() { 7 } 8 9 public void func3() { 10 } 11 }
1 public class Adaptor implements IAdapter { 2 3 private Adaptee adaptee; 4 5 public Adaptor(Adaptee adaptee) { 6 this.adaptee = adaptee; 7 } 8 9 @Override 10 public void func1() { 11 // 委托给adaptee实现 12 adaptee.f1(); 13 } 14 15 @Override 16 public void func2() { 17 // 重写func2 18 } 19 20 @Override 21 public void func3() { 22 // 委托给adaptee实现 23 adaptee.func3(); 24 } 25 }
你这两种方式该如何选择呢,一看接口数量,二看契合度。
- 如果接口数量不是很多,两个都可以。
- 如果接口数量较多,且Adaptee与IAdapter中的接口相似度高(契合度),推荐使用类适配器的方式,这样Adaptor的代码更简洁。
- 如果接口数量较多,但契合度不高,则推荐使用对适配器,因为基于组合的方式更加灵活。
适配器模式的使用场景
一般来说,适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷。
应用这种模式算是“无奈之举”。如果在设计初期,我们就能协调规避接口不兼容的问题,那这种模式就没有应用的机会了。
———————————————————————————————————————————————————————
1、封装有缺陷的接口设计
比如外部系统在设计上有缺陷(比如有大量静态方法、一个函数参数过多),而我们又不能改,那我们可以对其进行二次封装。———————————————————————————————————————————————————————
2、统一多个类的接口设计
假如你要对用户敏感词过滤,为了增加用户粘性,我们引入了多款第三方敏感词过滤系统,尽量的过滤多条敏感词。
但是,每个系统提供的过滤接口都是不同的。这就意味着我们没法复用一套逻辑来调用各个系统。
这个时候,我们就可以使用适配器模式,将所有系统的接口适配为统一的接口定义,这样我们可以复用调用敏感词过滤的代码。
1 /** 2 * A系统敏感词过滤 3 * 4 * @author zhoude 5 * @date 2020/4/11 13:00 6 */ 7 public class AThirdFilter { 8 9 /** 10 * text是原始文本,函数输出用***替换敏感词之后的文本 11 */ 12 public String filterSexyWords(String text) { 13 // 伪实现 14 return "***"; 15 } 16 17 public String filterPoliticalWords(String text) { 18 // 伪实现 19 return "+++"; 20 } 21 }
1 /** 2 * B系统敏感词过滤 3 * 4 * @author zhoude 5 * @date 2020/4/11 13:00 6 */ 7 public class BThirdFilter { 8 9 public String filter(String text) { 10 // 伪实现 11 return "***"; 12 } 13 }
1 /** 2 * C系统敏感词过滤 3 * 4 * @author zhoude 5 * @date 2020/4/11 13:00 6 */ 7 public class CThirdFilter { 8 9 public String filter(String text, String mask) { 10 // 伪实现 11 return mask; 12 } 13 }
1 public class Test { 2 3 private AThirdFilter aThirdFilter = new AThirdFilter(); 4 private BThirdFilter bThirdFilter = new BThirdFilter(); 5 private CThirdFilter cThirdFilter = new CThirdFilter(); 6 7 public String filter(String text) { 8 // 未使用适配器模式之前的代码:代码的可测试性、扩展性不好 9 String result = aThirdFilter.filterPoliticalWords(text); 10 result = aThirdFilter.filterSexyWords(result); 11 result = bThirdFilter.filter(result); 12 result = cThirdFilter.filter(result, "***"); 13 return result; 14 } 15 }
使用之后:
1 public interface IThirdFilter { 2 3 String filter(String text); 4 }
1 public class AThirdFilterImpl implements IThirdFilter { 2 3 private AThirdFilter filter; 4 5 public AThirdFilterImpl(AThirdFilter aThirdFilter) { 6 this.filter = aThirdFilter; 7 } 8 9 @Override 10 public String filter(String text) { 11 String result = filter.filterPoliticalWords(text); 12 result = filter.filterSexyWords(result); 13 return result; 14 } 15 }
1 public class BThirdFilterImpl implements IThirdFilter { 2 3 private BThirdFilter filter; 4 5 public BThirdFilterImpl(BThirdFilter aThirdFilter) { 6 this.filter = aThirdFilter; 7 } 8 9 @Override 10 public String filter(String text) { 11 return filter.filter(text); 12 } 13 }
1 public class CThirdFilterImpl implements IThirdFilter { 2 3 private CThirdFilter filter; 4 5 public CThirdFilterImpl(CThirdFilter aThirdFilter) { 6 this.filter = aThirdFilter; 7 } 8 9 @Override 10 public String filter(String text) { 11 return filter.filter(text, "***"); 12 } 13 }
1 public class Test { 2 3 private List<IThirdFilter> filterList = new ArrayList<>(); 4 5 public void addFilter(IThirdFilter filter) { 6 filterList.add(filter); 7 } 8 9 public String filter(String text) { 10 String result = text; 11 for (IThirdFilter filter : filterList) { 12 result = filter.filter(result); 13 } 14 return result; 15 } 16 }
———————————————————————————————————————————————————————
3、替换依赖的外部系统:当我们把项目中依赖的一个外部系统替换为另一个外部系统的时候,利用适配器模式,可以减少对代码的改动。
———————————————————————————————————————————————————————
4、适配不同的数据格式:如Java 中的 Arrays.asList()。
代理、桥接、装饰、适配器区别
- 代理模式:代理模式在不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控制访问,而非加强功能,这是它跟装饰器模式最大的不同。
- 桥接模式:桥接模式的目的是将接口部分和实现部分分离,从而让它们可以较为容易、也相对独立地加以改变。
- 装饰器模式:装饰者模式在不改变原始类接口的情况下,对原始类功能进行增强,并且支持多个装饰器的嵌套使用。
- 适配器模式:适配器模式是一种事后的补救策略。适配器提供跟原始类不同的接口,而代理模式、装饰器模式提供的都是跟原始类相同的接口。