主文章(所有java设计模式的目录) |
---|
https://blog.csdn.net/grd_java/article/details/122252696 |
源码位置 |
---|
码云:https://gitee.com/yin_zhipeng/design_mode.git |
GitHub: |
学习设计模式前必须知道的东西 |
---|
看待设计模式,要站在更大的角度(代码重用性、可读性、可扩展性、可靠性、程序高内聚,低耦合)来综合考虑看待,而不是功能实现的角度看待,不要觉得实现一个功能没必要这么麻烦
文章中给出的设计模式类图都是标准的实现方式,并不一定要完全遵守标准,所以只要设计思想符合,一个设计模式有多种实现方式,尤其是看别人源码的时候,不要用标准类图死扣
文章目录
1. 适配器模式(Adapter Pattern)
- 又名缺省适配器模式,将某个类的接口转换为客户端期望的另一个接口表示,主目的是兼容性,让原本接口不匹配不能一起工作的两个类,可以协同工作。别名为包装器(Wrapper)
- 适配器模式属于结构型模式
- 分三类:类适配器、对象适配器、接口适配器模式
通俗的讲就是将一个对象,通过不同适配器,转换为不同的对象,比如220V市电,通过各种变压器,转换为不同直流电
工作原理 |
---|
- 适配器模式:将一个类的接口转换成另一种接口,让
原本接口不兼容的类可以兼容
- 用户角度看不到被适配者,是解耦的
- 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
- 用户收到反馈结果,感觉只是和目标接口交互
1. 类适配器
- Adapter类(适配器),继承src类(源类),实现dst类接口(目标),完成src->dst的适配
- 假设src就是墙上的插座,220v交流电。dst,是目标,比如手机,我们需要5v直流电
- 那么Adapter就是充电器,将220v交流电适配为5v直流电,给手机使用
注意事项 |
---|
- java是单继承机制,类适配器需要继承源类这一点是一个缺点(如果我们将适配器也抽象成一个接口,然后传入具体实现类指定适配器是否可行呢?也OK)
- 源类,会在类适配器中暴露出来,增加使用成本
- 不过继承了源类,根据需求,重写源类方法,灵活性反而增强了
类图:com/yzpnb/design_mode/adapter_pattern/class_adapter/uml/Phone.puml |
---|
代码:com/yzpnb/design_mode/adapter_pattern/class_adapter/phone包下 |
---|
- 220V市电,源对象,很多产品不能直接使用它
- 5V直流电,目标对象,手机充电时可以使用的对象(不做具体实现,适配器将其转换)
- 适配器,继承220V类,我们直接可以获取它,实现5V接口,接口中规定我们要将220V适配成什么样
- 有了适配器,就可以给手机充电了
- 使用者,我们需要先选择充电器,然后给手机充电
2. 对象适配器(常用)
- 和类适配器基本相同,对适配器类做了一些修改
- 适配器类不再继承源类了,而是持有源类的实例,解决兼容性问题
- 也就是持有src类,实现dst类接口,完成src->dst的适配
- 根据“合成复用原则”,系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式
类图:com/yzpnb/design_mode/adapter_pattern/object_adapter/uml/Phone.puml |
---|
代码:com/yzpnb/design_mode/adapter_pattern/object_adapter/phone包下 |
---|
- 修改适配器类
- 其它都无需变动
3. 接口适配器
- 当
不需要去全部实现接口提供的方法时
,可以先
设计一个抽象类实现接口
,并为该接口中每个方法提供一个默认实现(空方法)
,那么该抽象类的子类就可以有选择地覆盖父类的某些方法
来实现需求- 适用与一个接口不想使用其所有的方法的情况
- 更加抽象的概念了,连适配器类都不需要了
程序:com/yzpnb/design_mode/adapter_pattern/interface_adapter/phone包下 |
---|
- 假设我在5V的接口中,添加两个方法,漏电和停电
- 抽象类,继承,空实现方法,这样其他类就可以有选择的使用这些方法
- 需要使用某些方法的类,直接继承实现,或者使用匿名内部类
2. 用到适配器模式的源码
Spring MVC的HandlerAdapter,使用到了适配器模式 |
---|
- 使用HandlerAdapter的原因是,处理器类型不同,有多重实现方式,调用方法是不确定的,若直接调用Controller方法,需要调用的时候就得不断使用if else进行判断是哪一子类,如果后面要扩展Controller,就得修改原来代码,违背OCP(开闭)原则
- 先看一下SpringMVC请求Controller流程,类图:com/atguigu/spring/UML/SpringMVC请求流程.puml
- 如何使用适配器模式的呢?(实在不想画这张类图了,太麻烦了)
- 进入DispatcherServlet
- MVC使用doDispatch方法处理来到的请求,然后通过getHandler方法,获取我们请求的Handler,拿到控制器(mappedHandler映射Controller)
- 拿到控制器(Controller映射)后,通过getHandlerAdapter方法获取相符合的适配器(不同的Handler,需要不同的适配器去调用相关方法)
- 先看看getHandlerAdapter怎么返回的,最后再看看返回的到底是什么
- 发现是一个for循环,依次遍历所有适配器,然后通过supports()方法判断,是否是一个类型,如果是就是返回适配器
- HandlerAdapter接口,看看具体有哪些适配器
- 拿到适配器后,又干了什么呢?原来是通过适配器,调用handle方法,返回ModelAndView(通过适配器调用controller的方法)。
3. 手写SpringMVC适配器设计模式,获取对应Controller
- Spring 定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类
- 适配器代替controller执行相应的方法
- 扩展Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展了
类图:com/atguigu/spring/springmvc/uml/springmvc.puml |
---|
- 好处,如果我们添加一个Controller种类,添加一个对应适配器种类,对于我们使用的DispatchServlet,没有任何影响
代码:com/atguigu/spring/springmvc包下 |
---|
- 先把需要适配的东西Controller高出来(Controller是5V直流电,我们想要适配成这个样子)
//多种Controller实现
public interface Controller {
}
//不同的适配种类
class HttpController implements Controller {
public void doHttpHandler() {
System.out.println("http...");
}
}
class SimpleController implements Controller {
public void doSimplerHandler() {
System.out.println("simple...");
}
}
class AnnotationController implements Controller {
public void doAnnotationHandler() {
System.out.println("annotation...");
}
}
- 把适配器搞出来(就是充电器),有多种适配器
///定义一个Adapter接口
public interface HandlerAdapter {
public boolean supports(Object handler);
public void handle(Object handler);
}
// 多种适配器类
class SimpleHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((SimpleController) handler).doSimplerHandler();
}
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}
}
class HttpHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((HttpController) handler).doHttpHandler();
}
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}
}
class AnnotationHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((AnnotationController) handler).doAnnotationHandler();
}
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}
}
- DispatchServlet
import java.util.ArrayList;
import java.util.List;
public class DispatchServlet {
//保存了所有适配器
public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();
//适配器放进去
public DispatchServlet() {
handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new HttpHandlerAdapter());
handlerAdapters.add(new SimpleHandlerAdapter());
}
//首先调用的就是doDispatch方法
public void doDispatch() {
// 此处模拟SpringMVC从request取handler的对象,
// 适配器可以获取到希望的Controller
HttpController controller = new HttpController();
// AnnotationController controller = new AnnotationController();
//SimpleController controller = new SimpleController();
// 得到对应适配器
HandlerAdapter adapter = getHandler(controller);
// 通过适配器执行对应的controller对应方法
adapter.handle(controller);
}
//根据Controller获取适配器,SpringMVC中是映射对象mappedHandler
public HandlerAdapter getHandler(Controller controller) {
//遍历:根据得到的controller(handler), 返回对应适配器
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(controller)) {
return adapter;
}
}
return null;
}
//这里我们人为直接调用doDispatch()
public static void main(String[] args) {
new DispatchServlet().doDispatch(); // http...
}
}