【设计模式】适配器模式,手动实现一个简单的 AOP 框架
通过这两个例子大家对适配器是什么应该有了一个大致的了解了吧,而我们今天要讲的内容是设计模式中常见的一种:适配器模式。
[](
)什么是适配器模式?
========================================================================
适配器模式(英语:adapter pattern)有时候也称包装样式或者包装(英语:wrapper)。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类能在一起工作,做法是将类自己的接口包裹在一个已存在的类中。
专业解释总是那么的不近人情,让人琢磨不透,我来举个简单的例子吧,假如你开发的系统现在正在升级,由 1.0 -> 2.0 许多接口都发生了翻天覆地的变化,由于兼容性的问题,并不能直接将老接口删除,但是老接口的实现确实严重拖慢了程序的效率,老接口和新接口的参数完全不一样,这个时候怎么办呢?有没有一个类可以帮忙做个中转,将老接口的数据转换成新接口的数据,然后调用新接口,这样效率是不是就快很多了呢?没错,适配器模式就是干这个的,他就是将两个原本不兼容的方法或者接口通过转接变成可以相互通信的工具。
看起来是不是很简单呢?适配器模式的原理确实很简单,那我们应该怎么去实现它呢?适配器模式的实现方式主要有两种:继承、组合,什么时候用继承,什么时候用组合呢?简单点来说就是类适配器使用继承关系实现,对象适配器使用组合关系实现,很抽象?没关系,我们一起用代码实现这两种方式,看完代码之后你就能理解这两种实现方式了。
废话不多说,先看第一种:类适配器。
package com.liuxing.adapter.adaptee;
/**
@ProjectName: hxjm
@Package: com.hxjm.fish
@ClassName: FishGwService
@Author: 流星 007
@Description: 需要转接的接口定义 类适配器 基于继承
csdn:https://blog.csdn.net/qq_33220089
今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
@Date: 2021/4/19 20:38
@Version: 1.0
*/
public interface ITarget {
void doSomthing1();
void doSomthing2();
void doSomthing3();
}
package com.liuxing.adapter.adaptee;
/**
@ClassName MyAdaptee
@Description 与 Itarget 定义的接口不兼容的类
csdn:https://blog.csdn.net/qq_33220089
今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
@Author ymy
@Date 2021/4/27 11:14
*/
public class MyAdaptee {
public void todo1(){
System.out.println("这是 todo1");
}
public void todo2(){
System.out.println("这是 todo2");
}
public void doSomthing3(){
System.out.println("这是 todo3");
}
}
package com.liuxing.adapter.adaptee;
/**
@ClassName MyAdaptor
@Description 适配器,将原本不兼容 Itarget 的接口转化为兼容 Itarget 的接口
csdn:https://blog.csdn.net/qq_33220089
今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
@Author ymy
@Date 2021/4/27 11:20
*/
public class MyAdaptor extends MyAdaptee implements ITarget {
@Override
public void doSomthing1() {
super.todo1();
}
@Override
public void doSomthing2() {
System.out.println("我是被重新实现的 dosomthing2");
}
}
第二种:对象适配器
package com.liuxing.adapter;
/**
@ClassName MyObjectAdaptor
@Description 适配器,对象适配器
@Author: 流星 007
@Date 2021/4/27 14:08
*/
public class MyObjectAdaptor implements ITarget {
private MyAdaptee myAdaptee;
public MyObjectAdaptor(MyAdaptee myAdaptee) {
this.myAdaptee = myAdaptee;
}
@Override
public void doSomthing1() {
//交给 MyAdaptee 实现
myAdaptee.todo1();
}
@Override
public void doSomthing2() {
System.out.println("我是被重新实现的 dosomthing2");
}
@Override
public void doSomthing3() {
myAdaptee.doSomthing3();
}
}
package com.liuxing.adapter;
import com.sun.corba.se.spi.oa.ObjectAdapter;
/**
@ClassName Test
@Description 测试
@Author: 流星 007
@Date 2021/4/27 13:56
*/
public class Test {
public static void main(String[] args) {
//类适配器
ITarget classAdaptor = new MyClassAdaptor();
classAdaptor.doSomthing1();
classAdaptor.doSomthing2();
classAdaptor.doSomthing3();
System.out.println("============================");
//对象适配器
ITarget objectAdaptor = new MyObjectAdaptor(new MyAdaptee());
objectAdaptor.doSomthing1();
objectAdaptor.doSomthing2();
objectAdaptor.doSomthing3();
}
}
ITarget:表示需要转接成的接口定义。
MyAdaptee:是不兼容 ITarget 接口定义的接口。
MyClassAdaptor:将 Adaptee 转化成一组符合 ITarget 接口定义的接口(类适配器)。
MyObjectAdaptor:将 Adaptee 转化成一组符合 ITarget 接口定义的接口(对象适配器)。
Test:测试类
输出结果如下
这是 todo1
我是被重新实现的 dosomthing2
这是 todo3
============================
这是 todo1
我是被重新实现的 dosomthing2
这是 todo3
Process finished with exit code 0
这两种实现方式都比较的巧妙,如果看不太明白的话可以结合着实际的应用场景再试试,个人觉得还是没有那么难理解,不过现在还有一个问题需要我们进一步分析,是什么呢?什么时
候使用类适配器,什么时候使用对象适配器呢?我们都说组合优于继承,当然是优先选择对象适配器啦,千万不要有这种想法,虽然组合优于继承,但这并不能说明任何情况组合都比继承适用,那我们如何来判断这两种实现方式呢?
标准主要有两个,第一个是 MyAdaptee 的接口个数,第二个则是 MyAdaptee 与 ITarget 的契合程度。这怎么理解呢?
MyAdaptee 接口较少,类适配器和对象适配器两者任选其一即可。
MyAdaptee 接口很多,但是大部分的接口 都是与 ITarget 相同,只是少部分存在区别,那么推荐使用类适配器(继承实现),改动代码量相对于对象适配器来说较少。
MyAdaptee 接口很多,并且大部分的接口都不相同,这个时候推荐使用对象适配器(组合实现),因为组合比继承更加的灵活。
[](
)适配器模式应用场景
========================================================================
适配器模式的应用场景还是比较多的,但是这种设计模式多用于补救,当程序设计出现问题,升级改变较大时,需要一个类似适配器的东西将他们连接起来,所以我们在正常的开发中使用这种模式的机会还是不多的,也希望同学们设计的程序都是很稳定,不会用到适配器来做补救。
敏感词过滤
升级兼容
架构设计缺陷
Slf4j 日志打印
外部依赖
应用场景太多,我拿敏感词过滤来做一个 demo 说明吧,敏感词大家应该都了解过,玩游戏的时候如果有一个队友超鬼了,那么其他队友就会对他进行亲切的问候,这个时候有些太过优雅的词会被 ”**“ 代替,这个就是我们说的敏感词过滤。
假设我们系统一共有三家敏感词过滤的厂商,但是他们的接口标准都不相同,一般的写法是什么样的呢?上代码
package com.liuxing.adapter.demo;
import org.apache.commons.lang3.ObjectUtils;
import java.util.ArrayList;
import java.util.List;
/**
@ClassName AliSensitiveWordFilter
@Description 某里云过滤
@Author liuxing007
@Date 2021/4/27 16:07
*/
public class AliSensitiveWordFilter {
private static final List<String> sensitiveWords = new ArrayList<>(3);
static {
sensitiveWords.add("垃圾");
sensitiveWords.add("废物");
sensitiveWords.add("滚");
}
/**
@param filterWordVo
@return java.lang.String
@Description 过滤
@Date 2021/4/27 16:34
*/
public String filterWord(FilterWordVo filterWordVo) {
if (ObjectUtils.isEmpty(filterWordVo)) {
return null;
}
String repWord = filterWordVo.getRepWord();
String word = filterWordVo.getWord();
for (String sensitiveWord : sensitiveWords) {
if (word.indexOf(sensitiveWord) >= 0) {
System.out.println("找到敏感词:" + sensitiveWord + ",直接替换");
word = word.replaceAll(sensitiveWord, repWord);
}
}
return word;
}
}
package com.liuxing.adapter.demo;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
/**
@ClassName DuSensitiveWordFilter
@Description 某度云过滤
@Author liuxing007
@Date 2021/4/27 16:07
*/
public class DuSensitiveWordFilter {
private static final List<String> sensitiveWords = new ArrayList<>(3);
static {
sensitiveWords.add("爸");
sensitiveWords.add("妈");
sensitiveWords.add("爷");
}
/**
@param word 需要过滤的内容
@param repWord 需要替换成什么
@return java.lang.String
@Description 过滤
@Date 2021/4/27 16:34
*/
public String filterWord(String word, String repWord) {
if (StringUtils.isAllEmpty(word, repWord)) {
return null;
}
for (String sensitiveWord : sensitiveWords) {
if (word.indexOf(sensitiveWord) >= 0) {
System.out.println("找到敏感词:" + sensitiveWord + ",直接替换");
word = word.replaceAll(sensitiveWord, repWord);
}
}
return word;
}
}
package com.liuxing.adapter.demo;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ClassName WeiSensitiveWordFilter
@Description 某为云过滤
@Author ymy
@Date 2021/4/27 16:07
*/
public class WeiSensitiveWordFilter {
private static Map<Integer, String> filterMap = new HashMap<>();
private static final List<String> sensitiveWords = new ArrayList<>(3);
static {
//将敏感词过滤为 *
filterMap.put(1, "*");
//将敏感词过滤为空字符串
filterMap.put(2, "");
//将敏感词过滤为 -
filterMap.put(3, "-");
sensitiveWords.add("干");
sensitiveWords.add("操");
sensitiveWords.add("怼");
}
/**
@param word 需要过滤的内容
@param type 需要替换成什么
@return java.lang.String
@Description 过滤
@Date 2021/4/27 16:34
*/
public String filterWord(String word, Integer type) {
if (StringUtils.isEmpty(word) || type == null) {
return null;
}
String repWord = filterMap.get(type);
for (String sensitiveWord : sensitiveWords) {
if (word.indexOf(sensitiveWord) >= 0) {
System.out.println("找到敏感词:" + sensitiveWord + ",直接替换");
word = word.replaceAll(sensitiveWord, repWord);
}
}
return word;
}
}
package com.liuxing.adapter.demo.normal;
import com.liuxing.adapter.demo.AliSensitiveWordFilter;
import com.liuxing.adapter.demo.DuSensitiveWordFilter;
import com.liuxing.adapter.demo.FilterWordVo;
import com.liuxing.adapter.demo.WeiSensitiveWordFilter;
/**
@ClassName SensitiveWordFilterService
@Description 敏感词过滤处理
@Author liuxing007
@Date 2021/4/27 17:28
*/
public class SensitiveWordFilterService {
private AliSensitiveWordFilter aliSensitiveWordFilter = new AliSensitiveWordFilter();
private DuSensitiveWordFilter duSensitiveWordFilter = new DuSensitiveWordFilter();
private WeiSensitiveWordFilter weiSensitiveWordFilter = new WeiSensitiveWordFilter();
public String filter(String word) {
FilterWordVo filterWordVo = FilterWordVo.builder().word(word).repWord("*").build();
word = aliSensitiveWordFilter.filterWord(filterWordVo);
word = duSensitiveWordFilter.filterWord(word, "*");
word = weiSensitiveWordFilter.filterWord(word, 1);
return word;
}
评论