适桥 组装 外 享代
适配器
泰国插座用的是两孔的(欧标) ,可以买个多功能转换插头 (适配器) ,这样就可以
使用了
基本介绍
-
适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表
示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同
工作。其别名为包装器(Wrapper) -
适配器模式属于结构型模式
-
主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
工作原理
-
适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼
容 -
从用户的角度看不到被适配者,是解耦的
- A—> 适配器—>B, A是看不到B的
-
用户调用适配器转化出来的目标接口方法, 适配器再调用被适配者的相关接口
方法 -
用户收到反馈结果,感觉只是和目标接口交互, 如图
1. 类适配器
基本介绍: Adapter类,通过继承 src类,实现 dst 类接口,完成src->dst的适配。
类适配器模式应用实例
- 应用实例说明
以生活中充电器的例子来讲解适配器,充电器本身相当于Adapter, 220V交流电
相当于src (即被适配者), 我们的目dst(即 目标)是5V直流电
被适配器类
//被适配的类
public class Voltage220V {
//输出220V的电压
public int output220V() {
int src = 220;
System.out.println("电压=" + src + "伏");
return src;
}
}
适配接口
//适配接口
public interface IVoltage5V {
public int output5V();
}
适配器类
//适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {
@Override
public int output5V() {
int srcV = output220V();
int dstV = srcV / 44 ; //转成 5v
return dstV;
}
}
使用 适配器接口
public class Phone {
//充电
public void charging(IVoltage5V iVoltage5V) {
if(iVoltage5V.output5V() == 5) {
System.out.println("电压为5V, 可以充电~~");
}
}
}
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
类适配器模式注意事项和细节
-
Java是单继承机制,所以类适配器需要继承src类这一点算是一个缺点, 因为这要
求dst必须是接口,有一定局限性; -
src类的方法在Adapter中都会暴露出来,也增加了使用的成本。
-
由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵
活性增强了。
2. 对象适配器
-
基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而
是持有src类的实例,以解决兼容性的问题。 即:持有 src类,实现 dst 类接口,
完成src->dst的适配 -
根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系。
-
对象适配器模式是适配器模式常用的一种
public class VoltageAdapter implements IVoltage5V {
private Voltage220V voltage220V; // 关联关系-聚合
//通过构造器,传入一个 Voltage220V 实例
public VoltageAdapter(Voltage220V voltage220v) {
this.voltage220V = voltage220v;
}
@Override
public int output5V() {
int dst = 0;
if(null != voltage220V) {
int src = voltage220V.output220V();//获取220V 电压
System.out.println("使用对象适配器,进行适配~~");
dst = src / 44;
System.out.println("适配完成,输出的电压为=" + dst);
}
return dst;
}
}
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
对象适配器模式注意事项和细节
-
对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。
根据合成复用原则, 使用组合替代继承, 所以它解决了类适配器必须继承src的
局限性问题,也不再要求dst必须是接口。 -
使用成本更低,更灵活。
3. 接口适配器
-
一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。
-
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接
口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆
盖父类的某些方法来实现需求 -
适用于一个接口不想使用其所有的方法的情况。
接口 和 抽象类
public interface Interface4 {
public void m1();
public void m2();
}
//在AbsAdapter 我们将 Interface4 的方法进行默认实现
public abstract class AbsAdapter implements Interface4 {
//默认实现
public void m1() {
}
public void m2() {
}
}
使用抽象类
AbsAdapter absAdapter = new AbsAdapter() {
//只需要去覆盖我们 需要使用 接口方法
@Override
public void m1() {
System.out.println("使用了m1的方法");
}
};
absAdapter.m1();
= m1()->{
System.out.println("使用了m1的方法");
};
4. spring MVC
自己实现
4.1 controller 和 实现
//多种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...");
}
}*/
4.2 适配器接口 和 实现
///定义一个Adapter接口
public interface HandlerAdapter {
public boolean supports(Object handler);
public void handle(Object handler);
}
class HttpHandlerAdapter implements HandlerAdapter {
//把对象 转为 HttpController,并调用 其接口
public void handle(Object handler) {
((HttpController) handler).doHttpHandler();
}
//如果是 HttpController ,就返回 true
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}
}
/*
// 多种适配器类
class SimpleHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((SimpleController) handler).doSimplerHandler();
}
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}
}
class AnnotationHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((AnnotationController) handler).doAnnotationHandler();
}
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}
}*/
4.3 整合 适配器 和 Handler
public class DispatchServlet {
//定义一个 list,放入 所有的 适配器
public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();
public DispatchServlet() {
//handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new HttpHandlerAdapter());
//handlerAdapters.add(new SimpleHandlerAdapter());
}
public void doDispatch() {
// 此处模拟SpringMVC从request取handler的对象,
// 适配器可以获取到希望的Controller
HttpController controller = new HttpController();
//new AnnotationController();
//new SimpleController();
//1. 先根据请求(mvc里),拿到 controller。
//mappedHandler = getHandler(processedRequest);
//2. 在根据 controller 获取对象的 adapter
//HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//3. 通过 adapter调用控制器里的方法
//mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 得到对应适配器
HandlerAdapter adapter = getHandler(controller);
// 通过适配器执行对应的controller对应方法
adapter.handle(controller);
}
public HandlerAdapter getHandler(Controller controller) {
//遍历:根据得到的controller(handler), 返回对应适配器
for (HandlerAdapter adapter : this.handlerAdapters) {
//handler instanceof HttpController 的话,返回 true
if (adapter.supports(controller)) {
return adapter;
}
}
return null;
}
public static void main(String[] args) {
new DispatchServlet().doDispatch(); // http...
}
}
源码分析
SpringMvc中的HandlerAdapter, 就使用了适配器模式
处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,
- 如果需要直接调用Controller方法,需要调用的时候就得不断是使用if else来进行判断是哪一种子类然后执行。
- 那么如果后面要扩展Controller,就得修改原来的代码,这样违背了OCP原则。
MVC流程
中央调度(DispatcherServlet) 控制器 是一个 servlet,
- 所有的 请求都需要 经过它,在 web.xml
- springMVC配置文件,这个文件 从形式 和 工作原理上 和 Spring 的 ioc容器文件类似
- applicationContext-mvc.xml [类 struts.xml]
UserTest 普通类,加入 注解 @controller,就成为一个 控制器,
- 在springmvc中 叫 Handler
- 浏览器组装好 参数(用户名 密码),请求 login
- 通过 HandlerMapping的处理,找到 用户希望请求的 Handler
- 即:DispatcherServlet 中的 doDispatch
- 执行 目标方法。适配器
- 访问一个结果,该 结果是 一个对象 ModelAndView
- 调用 Internal Resource View Resolver 来对 返回 ModelAndView进行解析,
- 就到指定的资源,该 对象 需要在 applicationContext-mvc.xml 配置
- 当然现在 肯定用 GetMapp这样注解
- Jsp 去执行 响应的代码
- 将 这个 字符串结果,给 tomcat,然后 tomcat对这个 字符串进行 包装
- 成 一个 静态的 html页面(字符串),返回给 浏览器。
- 浏览器 本身解析 返回 的 静态页面,并显示
整合流程类
- 大体的流程
public class DispatcherServlet extends FrameworkServlet {
//doDispatch方法
// 通过HandlerMapping来映射Controller
mappedHandler = getHandler(processedRequest);
//获取适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 通过适配器调用controller的方法并返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
- 详细的方法调用
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
ModelAndView mv = null;
Exception dispatchException = null;
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//根据请求,得到 一个 handler (控制器)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//通过控制器,返回一个 适配器。不同的 hander 用不同的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// Actually invoke the handler.
//通过 适配器,调用 controller 的响应方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
//返回的 就是顶层适配器的 接口。如下,现在有有6个实现
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
//调用的 是AbstractHandlerMethodAdapter
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler");
}
}
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
}
Abstract Handler Method Adapter
Annotation Method Handler Adapter
Http Request xx xx 有
Simple Controller xx xx 有
Simple Servlet xx xx
Request Mapping xx xx 有 。自:3.1,现在都是用这个
this.handlerAdapters现在有 4个值
1. RequestMapping Handler Adapter
2. Handler Function Adapter 未知
3. Http Request Handler Adapter
4. Simple Controller Handler Adapter
RequestMappingHandlerMapping
顶层适配器接口
public interface HandlerAdapter {
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
各种适配器实现
HandlerAdapter 的实现子类 有多种
- 使得每一种Controller有一种对应的适配器 实现类,
- 每种 handler 有不同的实现方式
public class HttpRequestHandlerAdapter implements HandlerAdapter {
public HttpRequestHandlerAdapter() {
}
public boolean supports(Object handler) {
return handler instanceof HttpRequestHandler;
}
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((HttpRequestHandler)handler).handleRequest(request, response);
return null;
}
}
注意事项和细节
-
三种命名方式,是根据 src是以怎样的形式给到Adapter(在Adapter里的形式)来
命名的。 -
类适配器:以类给到,在Adapter里,就是将src当做类,继承
对象适配器:以对象给到,在Adapter里,将src作为一个对象,持有
接口适配器:以接口给到,在Adapter里,将src作为一个接口,实现 -
Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。
-
实际开发中,实现起来不拘泥于我们讲解的三种经典形式
桥接模式
现在对不同手机类型的
不同品牌实现操作编程(比如:
开机、关机、上网,打电话等),
手机
- 折叠式
- 直立式
- 旋转式
华为 小米 Vivo 都有这三个款式
传统解决:
- phone
- 折叠
- 华为 小米
- 直立
- 又有 华为 小米
- 折叠
传统方案解决手机操作问题分析
-
扩展性问题(类爆炸), 如果我们再增加手机的样式(旋转式),就需要增加各个品
牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。 -
违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这
样增加了代码维护成本. -
解决方案-使用桥接模式
基本介绍
-
桥接模式(Bridge模式)是指:将实现与抽象放在两个不同的类层次中,使两个层
次可以独立改变。 -
是一种结构型设计模式
-
Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同
的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现
(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能
扩展
- client 调用抽象类 Abstraction (桥接类 和 下面接口 聚合关系,抽象类是调用者,接口是 被调用者)
- 抽象类 聚合 接口 Implementtor (行为实现的接口)
- 接口有 A ,B 两个实现 (行为的具体实现)
- 抽象类 有子类的实现,RefinedAbstraction
- 抽象类 聚合 接口 Implementtor (行为实现的接口)
接口 和 多个实现
//接口
public interface Brand {
void open();
}
public class XiaoMi implements Brand {
@Override
public void open() {
System.out.println(" 小米手机开机 ");
}
抽象类 和 多个实现
- 桥
public abstract class Phone {
//组合品牌
private Brand brand;
//构造器
public Phone(Brand brand) {
super();
this.brand = brand;
}
protected void open() {
this.brand.open();
}
}
- open找的 父类的 open,
- 父类open是接口,
- 最终的 实现是 接口的实现类
- 父类open是接口,
//折叠式手机类,继承 抽象类 Phone
public class FoldedPhone extends Phone {
//构造器
public FoldedPhone(Brand brand) {
super(brand);
}
public void open() {
super.open();
System.out.println(" 折叠样式手机 ");
}
}
//获取折叠式手机 (样式 + 品牌 )
Phone phone1 = new FoldedPhone(new XiaoMi());
phone1.open();
- 增加一个 样式写一个类
- 增加一个 品牌写一个类
JDBC
桥接模式在JDBC的源码剖析
- Jdbc 的 Driver接口,如果从桥接模式来看, Driver就是一个接口,下面可以有
MySQL的Driver, Oracle的Driver,这些就可以当做实现接口类
public class Driver extends NonRegisteringDriver implements
java.sql.Driver {
static {
try {
//1.注册驱动
//2. 调用DriverManager中的getConnection
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}//静态
}//类
public Driver() throws SQLException {
}
public class ConnectionImpl extends ConnectionPropertiesImpl
implements MySQLConnetion {
}
- java.sql.Connection
- com.mysql.jdbc.Connection 新接口废弃
- MySQLConnection 新接口变成了 顶层
- ConnectionImpl 具体的类,实现上面
- MySQLConnection 新接口变成了 顶层
- com.mysql.jdbc.Connection 新接口废弃
现在是:把原来的4层关系,改为了3层。连接:Connection 和 MysqlConnection,增加了过渡层:JdbcConnection
- java.sql.Connection
- com.mysql.cj.MysqlConnection
- interface JdbcConnection extends Connection, MysqlConnection
- com.mysql.cj.jdbc.ConnectionImpl implements JdbcConnection
- interface JdbcConnection extends Connection, MysqlConnection
没有抽象类,直接用 DriverManager 桥接,拿到了 连接
- getConnection ,根据注册的驱动 不一样,返回 不同的驱动
- 依赖了 java.sql.Connection
- client 使用 DriverManager
注意事项和细节
-
实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实
现部分独立开来, 这有助于系统进行分层设计,从而产生更好的结构化系统。 -
对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部
分由具体业务来完成。 -
桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
-
桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,
要求开发者针对抽象进行设计和编程 -
桥接模式要求正确识别出系统中两个独立变化的维度(抽象 和 实现),因此其使用范围有一定的局
限性,即需要有这样的应用场景。
桥接模式其它应用场景
-
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥
接模式尤为适用. -
常见的应用场景:
-JDBC驱动程序 -
-银行转账系统
转账分类: 网上转账,柜台转账, AMT转账
转账用户类型:普通用户,银卡用户,金卡用户… -
-消息管理
消息类型:即时消息,延时消息
消息分类: 手机短信,邮件消息, QQ消息…