设计模式08-适配器模式与桥接模式详解
1.8设计模式-适配器模式与桥接模式详解
1.8.1.适配器模式详解
时长:1h6min
学习目标
》掌握适配器和桥接模式的应用场景
》重构第三方登录自由适配的业务场景
》了解模式在源码中应用
》优缺点
8.1.1.适配器模式的定义
定义:
适配器模式,Adapter Pattern,又称变压器模式。它是功能是一个接口变成客户所期望的另一种接口,从而使
原本因接口不匹配而导致无法在一起工作的两个类能够一起工作。
属于结构型设计模式。
生活中适配器模式的应用:
》两脚插转三角插座
》手机充电接口
》显示器转接头
8.1.1.1.应用场景
1.已经存在的类,它的方法与需求不匹配(方法结果相同或相似的情况)
2.适配器模式不是软件设计阶段考虑的设计模式,而是随着软件维护,由于不同产品,不同厂家造成
功能类似而接口不相同的情况下的解决方案。
8.1.2.适配器模式的实现方案
8.1.2.1.通用写法
适配器有3个角色,目标角色【期望兼容的角色】,原角色【系统中已存在角色】,适配器
适配器有3种形式:
》类适配器
》对象适配器
》接口适配器
1.类适配器写法
【1】标准实现类图
2.对象适配器写法
【1】标准类图
【2】代码实现
A.适配原角色
package com.wf.adapter.general.objectadapter; /** * @ClassName Adaptee * @Description 适配原角色 * @Author wf * @Date 2020/6/9 14:47 * @Version 1.0 */ public class Adaptee { public int specificRequest(){ return 220; } }
B.目标对象【实现是接口】
package com.wf.adapter.general.objectadapter; /** * @ClassName Target * @Description 目标对象 * @Author wf * @Date 2020/6/9 14:48 * @Version 1.0 */ public interface Target { int request(); }
C.适配器类
package com.wf.adapter.general.objectadapter; /** * @ClassName Adapter * @Description 对象适配器,组合引用原角色,也得实现目标对象的功能 * @Author wf * @Date 2020/6/9 14:48 * @Version 1.0 */ public class Adapter implements Target { //组合引用 private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public int request() { return adaptee.specificRequest() / 10; } }
D.测试类
package com.wf.adapter.general.objectadapter; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/6/9 14:49 * @Version 1.0 */ public class Test { public static void main(String[] args) { Target adapter = new Adapter(new Adaptee()); int result = adapter.request(); System.out.println("最终结果:"+result); } }
说明:
客户调用时,使用多态的方式。如此,就只会出现接口的功能,而不会出现原角色的功能调用。
3.接口适配器写法
它的使用场景:
它是用于解决,需要适配功能很多的情况,并且都与适配原角色相关,如果针对每一个功能都写一个适配器,就会出现很多的类,
造成类臃肿。
所以,就想到把这多个功能,归类到一个目标对象中,统一创建一个接口适配器,就可以达到一个类适配多个功能的目的。
解决方案:通过接口适配器,只适配需要的接口方法。
代码如下所示:
【1】适配原角色
package com.wf.adapter.general.interfaceadapter; /** * @ClassName Adaptee * @Description 适配原角色 * @Author wf * @Date 2020/6/9 14:47 * @Version 1.0 */ public class Adaptee { public int specificRequest(){ return 220; } }
【2】目标对象
package com.wf.adapter.general.interfaceadapter; /** * @ClassName Target * @Description 目标对象 * @Author wf * @Date 2020/6/9 14:48 * @Version 1.0 */ public interface Target { int request0(); int request1(); int request2(); int request3(); int request4(); }
说明:
可以看到,新增目标对象需要很多方法,并且都与适配原角色有关联。
因此,把这些方法,统一放到一个目标接口中,而不是定义多个目标类。
【3】适配器类
package com.wf.adapter.general.interfaceadapter; /** * @ClassName Adapter * @Description 接口适配器,组合引用原角色,也得实现目标对象的功能 * @Author wf * @Date 2020/6/9 14:48 * @Version 1.0 */ public class Adapter implements Target { //组合引用 private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public int request0() { return 0; } @Override public int request1() { return 0; } @Override public int request2() { return 0; } @Override public int request3() { return 0; } @Override public int request4() { return 0; } }
【4】测试类
package com.wf.adapter.general.interfaceadapter; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/6/9 14:49 * @Version 1.0 */ public class Test { public static void main(String[] args) { Target adapter = new Adapter(new Adaptee()); int result = adapter.request0(); System.out.println("最终结果:"+result); result = adapter.request2(); System.out.println("最终结果:"+result); } }
8.1.2.2.具体实例
1.类适配器的使用
这里使用变压器模式来,进行示例说明。
手机充电器,就是典型的适配器模式。适配器,最初是电工学上一个术语。后来被发展为软件术语。
手机充电器,获得手机需要的直流电【5v电压】,这可以当作目标对象。
而家电电压是220v交流电,可以视为适配器原角色。
充电器,把220v交流电压,转换为5v直流电压。就是适配器。
系统实现代码如下:
【1】适配原角色,220v交流电压
package com.wf.adapter.demo.classadapter; /** * @ClassName AC220 * @Description 交流电压220V * @Author wf * @Date 2020/6/3 14:36 * @Version 1.0 */ public class AC220 { public int outputAC220V(){ int output = 220; System.out.println("输出电压:"+output+" V"); return output; } }
【2】定义目标对象【实际是接口】
package com.wf.adapter.demo.classadapter; /** * @ClassName DC5 * @Description 直流电压5v * @Author wf * @Date 2020/6/3 14:39 * @Version 1.0 */ public interface DC5 { int output5V(); }
【3】充电器,适配器
package com.wf.adapter.demo.classadapter; /** * @ClassName PowerAdapter * @Description 电源适配器 * @Author wf * @Date 2020/6/3 14:40 * @Version 1.0 */ public class PowerAdapter extends AC220 implements DC5 { @Override public int output5V() { int adapterInput = super.outputAC220V(); int adapterOutput = adapterInput / 44; System.out.println("使用Adapter输入AC"+adapterInput + "V,输出DC["+adapterOutput +"]V"); return adapterOutput; } }
说明:
适配器类中,继承适配原角色,实现目标接口
实现接口的功能,调用原角色的功能,并进行扩展。
【4】测试类
package com.wf.adapter.demo.classadapter; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/6/3 14:44 * @Version 1.0 */ public class Test { public static void main(String[] args) { PowerAdapter adapter = new PowerAdapter(); adapter.output5V(); } }
测试结果如下:
说明:
由于适配器,继承原角色,并实现目标接口。所以,适配器具有两者的功能。
显然,有些违背最少知道原则。
因此,提出对象适配器写法。它的实现思路:
弃用继承结构,而采用组合方式。
2.对象适配器写法,改写系统
【1】适配原角色
不需要修改,如下所示:
package com.wf.adapter.demo.objectadapter; /** * @ClassName AC220 * @Description 交流电压220V * @Author wf * @Date 2020/6/3 14:36 * @Version 1.0 */ public class AC220 { public int outputAC220V(){ int output = 220; System.out.println("输出电压:"+output+" V"); return output; } }
【2】目标对象
代码逻辑不变,如下所示:
package com.wf.adapter.demo.objectadapter; /** * @ClassName DC5 * @Description 直流电压5v * @Author wf * @Date 2020/6/3 14:39 * @Version 1.0 */ public interface DC5 { int output5V(); }
【3】适配器类
package com.wf.adapter.demo.objectadapter; /** * @ClassName PowerAdapter * @Description 电源适配器 * @Author wf * @Date 2020/6/3 14:40 * @Version 1.0 */ public class PowerAdapter implements DC5 { private AC220 ac220; public PowerAdapter(AC220 ac220) { this.ac220 = ac220; } @Override public int output5V() { int adapterInput = ac220.outputAC220V(); int adapterOutput = adapterInput / 44; System.out.println("使用Adapter输入AC"+adapterInput + "V,输出DC["+adapterOutput +"]V"); return adapterOutput; } }
说明:
采用组合引用,来代替继承方式。
【4】测试类
package com.wf.adapter.demo.objectadapter; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/6/3 14:44 * @Version 1.0 */ public class Test { public static void main(String[] args) { DC5 adapter = new PowerAdapter(new AC220()); adapter.output5V(); } }
3.接口适配器案例
针对上面的系统,适配器需要转换多种电压,如:5v,12v,24v...
只需要把这多个功能,统一归集到一个目标类,然后一个适配器就可以实现。
【1】适配原角色
package com.wf.adapter.demo.interfaceadapter; /** * @ClassName AC220 * @Description 交流电压220V * @Author wf * @Date 2020/6/3 14:36 * @Version 1.0 */ public class AC220 { public int outputAC220V(){ int output = 220; System.out.println("输出电压:"+output+" V"); return output; } }
【2】目标对象
内部提供多个功能:
package com.wf.adapter.demo.interfaceadapter; /** * @ClassName DC * @Description 目标类,实现多个转换功能 * @Author wf * @Date 2020/6/9 16:04 * @Version 1.0 */ public interface DC { int output5V(); int output12V(); int output24V(); int output36V(); }
【3】适配器
package com.wf.adapter.demo.interfaceadapter; import com.wf.adapter.demo.objectadapter.AC220; import com.wf.adapter.demo.objectadapter.DC5; /** * @ClassName PowerAdapter * @Description 电源适配器 * @Author wf * @Date 2020/6/3 14:40 * @Version 1.0 */ public class PowerAdapter implements DC { private AC220 ac220; public PowerAdapter(AC220 ac220) { this.ac220 = ac220; } @Override public int output5V() { int adapterInput = ac220.outputAC220V(); int adapterOutput = adapterInput / 44; System.out.println("使用Adapter输入AC"+adapterInput + "V,输出DC["+adapterOutput +"]V"); return adapterOutput; } @Override public int output12V() { return 0; } @Override public int output24V() { return 0; } @Override public int output36V() { return 0; } }
【4】测试类
package com.wf.adapter.demo.interfaceadapter; import com.wf.adapter.demo.objectadapter.AC220; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/6/9 16:12 * @Version 1.0 */ public class Test { public static void main(String[] args) { DC adapter = new PowerAdapter(new AC220()); adapter.output5V(); } }
8.1.2.3.开发中适配器模式应用案例
1.系统需求
是一个管理平台登录场景。在大量的老系统中,需要用户注册,然后登录,才能使用平台。
但是,当大量用户使用时,这种帐号登录的方式,管理起来反而很复杂。
所以,从而使用员工的qq,或微信号进行登录,从而免去注册的过程。
然而,系统的注册,登录功能仍然不能修改,这时就可以使用适配器模式,来解决系统兼容性问题。
2.代码实现
【1】原有登录系统功能
A.业务类,员工类
package com.wf.adapter.demo.passport; /** * @ClassName Member * @Description 成员类 * @Author wf * @Date 2020/6/9 16:18 * @Version 1.0 */ public class Member { private String username; private String password; private String mid; private String info; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getMid() { return mid; } public void setMid(String mid) { this.mid = mid; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } }
B.结果集pojo
package com.wf.adapter.demo.passport; /** * @ClassName ResultMsg * @Description 结果集 * @Author wf * @Date 2020/6/9 16:19 * @Version 1.0 */ public class ResultMsg { private int code; private String msg; private Object data; public ResultMsg(int code, String msg,String data) { this.code = code; this.msg = msg; this.data = data; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
C.登录注册服务
package com.wf.adapter.demo.passport; /** * @ClassName PassportService * @Description 登录,注册服务 * @Author wf * @Date 2020/6/9 16:18 * @Version 1.0 */ public class PassportService { public ResultMsg register(String username,String password){ return new ResultMsg(200,"注册成功",null); } public ResultMsg login(String username,String password){ return null; } }
【2】适配器扩展系统
A.目标类【定义为接口】
这里需要实现第三方登录,如:qq登录。就需要使用腾训提供open api【需要传参openId】,如下所示:
package com.wf.adapter.demo.passport; /** * @ClassName IPassortForThird * @Description 第三方登录,目标类 * @Author wf * @Date 2020/6/10 10:06 * @Version 1.0 */ public interface IPassportForThird { /** * qq登录 * @param openId * @return */ ResultMsg loginForQQ(String openId); /** * 微信号登录 * @param openId * @return */ ResultMsg loginForWeChat(String openId); /** * 内部员工token登录 * @param token * @return */ ResultMsg loginForToken(String token); /** * 手机号+验证码登录 * @param phone * @param code * @return */ ResultMsg loginForTelephone(String phone, String code); }
B.适配器类
这里使用,类适配器实现。
package com.wf.adapter.demo.passport; /** * @ClassName PassportForThirdAdapter * @Description 第三方登录适配器,类适配器 * @Author wf * @Date 2020/6/10 10:15 * @Version 1.0 */ public class PassportForThirdAdapter extends PassportService implements IPassportForThird { @Override public ResultMsg loginForQQ(String openId) { return loginForRegister(openId,null); } @Override public ResultMsg loginForWeChat(String openId) { return loginForRegister(openId,null); } @Override public ResultMsg loginForToken(String token) { return loginForRegister(token,null); } @Override public ResultMsg loginForTelephone(String phone, String code) { return loginForRegister(phone,null); } /** * 提供一个老的登录逻辑,先注册,再登录 * 当使用第三方登录时,不能够得到密码,处理办法是:如果密码为null,约定一个特殊密码。 * 后台做一个标识,只要得到这个特殊密码,都表示第三方登录,走特殊流程 * @param username * @param password * @return */ private ResultMsg loginForRegister(String username, String password){ if(null == password){ password = "THIRD_EMPTY"; } super.register(username,password); return super.login(username,password); } }
3.测试类
package com.wf.adapter.demo.passport; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/6/10 10:25 * @Version 1.0 */ public class Test { public static void main(String[] args) { IPassportForThird service = new PassportForThirdAdapter(); //给一个openId,不会是qq号,为了保护用户隐私 service.loginForQQ("owpgnw"); } }
说明:
功能基本完成。但是,由于适配器内部需要进行各种openAPI接入,导致类的职责过重。
而且,当系统进行需求扩展时,如:需要支持抖音帐号登录。就需要修改适配器原有代码,不符合开闭原则。
4.扩展需求,增加抖音帐号登录
由于功能扩展,原来的适配器类,可能职责过重,依赖大量openAPI.
因此,可以对系统进行拆分,针对每一种登录,实现一个适配器,来处理登录逻辑。
【1】目标类【实为接口】
package com.wf.adapter.demo.passport.v2; import com.wf.adapter.demo.passport.ResultMsg; /** * @ClassName IPassportForThird * @Description 第三方登录,目标类 * @Author wf * @Date 2020/6/10 10:06 * @Version 1.0 */ public interface IPassportForThird { /** * qq登录 * @param openId * @return */ ResultMsg loginForQQ(String openId); /** * 微信号登录 * @param openId * @return */ ResultMsg loginForWeChat(String openId); /** * 内部员工token登录 * @param token * @return */ ResultMsg loginForToken(String token); /** * 手机号+验证码登录 * @param phone * @param code * @return */ ResultMsg loginForTelephone(String phone, String code); }
【2】适配器中转类
package com.wf.adapter.demo.passport.v2; import com.wf.adapter.demo.passport.PassportService; import com.wf.adapter.demo.passport.ResultMsg; /** * @ClassName PassportForThirdAdapter * @Description 第三方登录适配,中间实现类 * @Author wf * @Date 2020/6/10 10:15 * @Version 1.0 */ public class PassportForThirdAdapter implements IPassportForThird { @Override public ResultMsg loginForQQ(String openId) { return processLogin(openId,LoginForQQAdapter.class); } @Override public ResultMsg loginForWeChat(String openId) { return processLogin(openId,LoginForWeChatAdapter.class); } @Override public ResultMsg loginForToken(String token) { return processLogin(token,LoginForTokenAdapter.class); } @Override public ResultMsg loginForTelephone(String phone, String code) { return processLogin(phone,LoginForTelephoneAdapter.class); } private ResultMsg processLogin(String id, Class<? extends ILoginAdapter> clazz){ try { ILoginAdapter adapter = clazz.newInstance(); if(adapter.support(adapter)){ return adapter.login(id,adapter); } }catch (Exception e){ e.printStackTrace(); } return null; } }
【3】顶层适配器接口
package com.wf.adapter.demo.passport.v2; import com.wf.adapter.demo.passport.ResultMsg; /** * @ClassName ILoginAdapter * @Description 适配器公共接口 * @Author wf * @Date 2020/6/10 10:36 * @Version 1.0 */ public interface ILoginAdapter { /** * 匹配适配器子类实现 * @param object * @return */ boolean support(Object object); /** * 登录逻辑 * @param id * @param adapter * @return */ ResultMsg login(String id,Object adapter); }
【4】单个适配器实现
package com.wf.adapter.demo.passport.v2; /** * @ClassName LoginForQQAdapter * @Description 适配器 * @Author wf * @Date 2020/6/10 10:45 * @Version 1.0 */ public class LoginForQQAdapter extends AbstractAdapter { @Override public boolean support(Object adapter) { return adapter instanceof LoginForQQAdapter; } } package com.wf.adapter.demo.passport.v2; import com.wf.adapter.demo.passport.ResultMsg; /** * @ClassName LoginForTelephoneAdapter * @Description TODO * @Author wf * @Date 2020/6/10 14:02 * @Version 1.0 */ public class LoginForTelephoneAdapter extends AbstractAdapter { @Override public boolean support(Object adapter) { return adapter instanceof LoginForTelephoneAdapter; } } package com.wf.adapter.demo.passport.v2; /** * @ClassName LoginForTokenAdapter * @Description 内部员工登录,适配器 * @Author wf * @Date 2020/6/10 14:00 * @Version 1.0 */ public class LoginForTokenAdapter extends AbstractAdapter { @Override public boolean support(Object adapter) { return adapter instanceof LoginForTokenAdapter; } } package com.wf.adapter.demo.passport.v2; /** * @ClassName loginForWeChatAdapter * @Description 第三方登录,微信登录 * @Author wf * @Date 2020/6/10 13:57 * @Version 1.0 */ public class LoginForWeChatAdapter extends AbstractAdapter { @Override public boolean support(Object adapter) { return adapter instanceof LoginForWeChatAdapter; } }
【5】抽取适配器公共实现
package com.wf.adapter.demo.passport.v2; import com.wf.adapter.demo.passport.PassportService; import com.wf.adapter.demo.passport.ResultMsg; /** * @ClassName AbstractAdapter * @Description 提取公共代码 * @Author wf * @Date 2020/6/10 10:52 * @Version 1.0 */ public abstract class AbstractAdapter extends PassportService implements ILoginAdapter{ /** * 提供一个老的登录逻辑,先注册,再登录 * 当使用第三方登录时,不能够得到密码,处理办法是:如果密码为null,约定一个特殊密码。 * 后台做一个标识,只要得到这个特殊密码,都表示第三方登录,走特殊流程 * @param username * @param password * @return */ protected ResultMsg loginForRegister(String username, String password){ if(null == password){ password = "THIRD_EMPTY"; } super.register(username,password); return super.login(username,password); } @Override public ResultMsg login(String id, Object adapter) { return loginForRegister(id,null); } }
【6】测试类
package com.wf.adapter.demo.passport.v2; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/6/10 14:05 * @Version 1.0 */ public class Test { public static void main(String[] args) { IPassportForThird adapter = new PassportForThirdAdapter(); adapter.loginForQQ("qpgqjgpwokoekfkfod"); } }
说明:
如果要扩展抖音帐号登录,需要修改目标接口及实现。并且增加一个适配器实现子类。
这只能在一定程度上,实现动态扩展的目的。但并不能够真正地满足开闭原则。
【7】系统类图
2.1.3.适配器模式在源码中应用
2.1.3.1.spring中aop模块
public interface AdvisorAdapter { boolean supportsAdvice(Advice var1); MethodInterceptor getInterceptor(Advisor var1); }
实现类如下:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable { MethodBeforeAdviceAdapter() { } public boolean supportsAdvice(Advice advice) { return advice instanceof MethodBeforeAdvice; } public MethodInterceptor getInterceptor(Advisor advisor) { MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice(); return new MethodBeforeAdviceInterceptor(advice); } }
2.1.3.2.spring中mvc模块
public interface HandlerAdapter { boolean supports(Object var1); @Nullable ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception; long getLastModified(HttpServletRequest var1, Object var2); }
适配器各实现子类:
实现子类:
public class SimpleControllerHandlerAdapter implements HandlerAdapter { public SimpleControllerHandlerAdapter() { } public boolean supports(Object handler) { return handler instanceof Controller; } @Nullable public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller)handler).handleRequest(request, response); } public long getLastModified(HttpServletRequest request, Object handler) { return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L; } }
适配器的使用:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { Iterator var2 = this.handlerAdapters.iterator(); while(var2.hasNext()) { HandlerAdapter ha = (HandlerAdapter)var2.next(); if (this.logger.isTraceEnabled()) { this.logger.trace("Testing handler adapter [" + ha + "]"); } if (ha.supports(handler)) { //判断使用哪一种适配器 return ha; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
2.1.4.适配器模式总结
2.1.4.1.优点,缺点总结
优点:
能提高类的透明性和复用性,现有的类复用且不需要改变。
目标类和适配器类解耦,提高程序的扩展性。
在很多业务场景中符合开闭原则。
缺点:
》适配器编写过程,需要全面考虑,可能会增加系统的复杂性。
》增加代码的阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
1.8.2.桥接模式详解
时长:58min
8.2.1.桥接模式定义
定义:
Bridge Pattern,也称桥梁模式,接口模式,或柄体(Handle and Body)模式,是将抽象部分与它的具体
实现部分分离,使它们可以独立变化的。
通过组合的方式建立两个类之间的联系,而不使用继承。
属于结构型模式。
桥接模式在生活中的应用场景:
1.连接两个空间的拱桥
2.虚拟网络与真实网络的桥接
8.2.1.1.适用场景
1.在抽象和具体实现之间需要增加更多的灵活性的场景
2.一个类存在两个(或多个)独立变化的维度,而这两个(或多个)维度都需要独立进行扩展。
3.不希望继承,或因为多层继承导致系统类的个数剧增【多用组合引用】
8.2.2.桥接模式的写法
8.2.2.1.通用写法
1.系统类图设计
2.代码实现
A.顶层接口
package com.wf.bridge.general; /** * @ClassName IImplementor * @Description 顶层接口定义 * @Author wf * @Date 2020/6/10 15:28 * @Version 1.0 */ public interface IImplementor { void operationImpl(); }
B.接口实现
package com.wf.bridge.general; /** * @ClassName ConcreteImplementorA * @Description 实现 * @Author wf * @Date 2020/6/10 15:29 * @Version 1.0 */ public class ConcreteImplementorA implements IImplementor { @Override public void operationImpl() { System.out.println("I'm ConcreteImplementorA"); } } package com.wf.bridge.general; /** * @ClassName ConcreteImplementorB * @Description 接口实现B * @Author wf * @Date 2020/6/10 15:29 * @Version 1.0 */ public class ConcreteImplementorB implements IImplementor { @Override public void operationImpl() { System.out.println("I'm ConcreteImplementorB"); } }
C.抽象类组合引用接口实例
package com.wf.bridge.general; /** * @ClassName Abstraction * @Description 抽象类,组合引用接口实例 * @Author wf * @Date 2020/6/10 15:27 * @Version 1.0 */ public abstract class Abstraction{ protected IImplementor mImplementor; public Abstraction(IImplementor mImplementor) { this.mImplementor = mImplementor; } public void operation(){ this.mImplementor.operationImpl(); } }
说明:
这里很像是包装器模式。
D.修改抽象实现
package com.wf.bridge.general; /** * @ClassName RefinedAbstraction * @Description 修正抽象 * @Author wf * @Date 2020/6/10 15:31 * @Version 1.0 */ public class RefinedAbstraction extends Abstraction { public RefinedAbstraction(IImplementor mImplementor) { super(mImplementor); } @Override public void operation() { super.operation(); System.out.println("refined operation"); } }
E.测试类
package com.wf.bridge.general; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/6/10 15:32 * @Version 1.0 */ public class Test { public static void main(String[] args) { RefinedAbstraction abstraction = new RefinedAbstraction(new ConcreteImplementorA()); abstraction.operation(); } }
8.2.2.2.桥接模式具体案例应用
1.需求分析
在前面的抽象工厂模式学习中,涉及产品簇和产品等级的实现案例。
产品簇,可以理解为产品的实现维度。
产品等级,理解为产品的抽象维度。
从而,可以使用桥接模式,将两种关系联系起来。
具体需求:
针对学习课程Course,课程内容有:笔记,源码,ppt,课后作业【理解为产品抽象】
不同类型的课程:java,Python,AI,大数据。。。【理解为产品实现】
2.代码实现
【1】定义产品实现顶层接口--课程
package com.wf.bridge.demo.course; /** * @ClassName ICourse * @Description 课程,顶层接口定义 * @Author wf * @Date 2020/6/10 15:58 * @Version 1.0 */ public interface ICourse { }
实现类:
package com.wf.bridge.demo.course; /** * @ClassName JavaCourse * @Description java课程 * @Author wf * @Date 2020/6/10 16:00 * @Version 1.0 */ public class JavaCourse implements ICourse { } package com.wf.bridge.demo.course; /** * @ClassName PythonCourse * @Description Python课程 * @Author wf * @Date 2020/6/10 16:01 * @Version 1.0 */ public class PythonCourse implements ICourse { }
【2】增加第二个维度的接口及实现----笔记
package com.wf.bridge.demo.course; /** * @ClassName INote * @Description 笔记接口 * @Author wf * @Date 2020/6/10 16:02 * @Version 1.0 */ public interface INote { void edit(); } package com.wf.bridge.demo.course; /** * @ClassName JavaNote * @Description java课程笔记 * @Author wf * @Date 2020/6/10 16:03 * @Version 1.0 */ public class JavaNote implements INote { @Override public void edit() { } } package com.wf.bridge.demo.course; /** * @ClassName PythonNote * @Description Python笔记 * @Author wf * @Date 2020/6/10 16:03 * @Version 1.0 */ public class PythonNote implements INote { @Override public void edit() { } }
【3】增加第三个维度的接口及实现----视频
package com.wf.bridge.demo.course; /** * @ClassName IVideo * @Description 视频接口 * @Author wf * @Date 2020/6/10 16:04 * @Version 1.0 */ public interface IVideo { } package com.wf.bridge.demo.course; /** * @ClassName JavaVideo * @Description TODO * @Author wf * @Date 2020/6/10 16:05 * @Version 1.0 */ public class JavaVideo implements IVideo { } package com.wf.bridge.demo.course; /** * @ClassName PythonVideo * @Description TODO * @Author wf * @Date 2020/6/10 16:05 * @Version 1.0 */ public class PythonVideo implements IVideo { }
说明:
现在,产品的三个维度是相互独立的,互不相关。
想要它们关联起来,因此,使用桥接模式,创建抽象,引用接口实例。、
【4】创建抽象,引用接口实现
package com.wf.bridge.demo.course; /** * @ClassName AbstractCourse * @Description 课程抽象 * @Author wf * @Date 2020/6/10 16:14 * @Version 1.0 */ public class AbstractCourse implements ICourse { private IVideo video; private INote note; public void setVideo(IVideo video) { this.video = video; } public void setNote(INote note) { this.note = note; } @Override public String toString() { return "AbstractCourse{" + "video=" + video + ", note=" + note + '}'; } }
说明:
因为Course存在不同实现,当提供这样一个抽象之后,原来的课程实现,就需要修改。
不是实现接口,而是直接继承这个抽象,修改后如下所示:
package com.wf.bridge.demo.course; /** * @ClassName JavaCourse * @Description java课程 * @Author wf * @Date 2020/6/10 16:00 * @Version 1.0 */ public class JavaCourse extends AbstractCourse{ } package com.wf.bridge.demo.course; /** * @ClassName PythonCourse * @Description Python课程 * @Author wf * @Date 2020/6/10 16:01 * @Version 1.0 */ public class PythonCourse extends AbstractCourse { }
此时,系统类图所示:
8.2.2.3.桥接模式具体案例实现二
1.系统需求分析
在我们日常的办公过程中,存在大量的消息同步,发送问题。
发送消息,可能采取不同的方式,如:邮件,短信,系统内消息
消息的类型,有不同:普通消息,加急消息,特急消息。
如下图示:
2.代码实现
【1】定义消息顶层接口
package com.wf.bridge.demo.message; /** * @ClassName IMessage * @Description 顶层消息接口 * @Author wf * @Date 2020/6/10 16:33 * @Version 1.0 */ public interface IMessage { void send(String content, String user); }
【2】消息发送方式子类实现
package com.wf.bridge.demo.message; /** * @ClassName EmailMessage * @Description 邮件方式发消息 * @Author wf * @Date 2020/6/10 16:35 * @Version 1.0 */ public class EmailMessage implements IMessage { @Override public void send(String content,String user) { System.out.println("使用邮件发送消息【"+content+"】,给【"+user+"】"); } } ackage com.wf.bridge.demo.message; /** * @ClassName SmsMessage * @Description 短信方式发送消息 * @Author wf * @Date 2020/6/10 16:40 * @Version 1.0 */ public class SmsMessage implements IMessage{ @Override public void send(String content, String user) { System.out.println("使用短信发送消息【"+content+"】,给【"+user+"】"); } }
【3】抽象引用消息实现
package com.wf.bridge.demo.message; /** * @ClassName AbstractMessage * @Description 消息类型,抽象 * @Author wf * @Date 2020/6/10 16:44 * @Version 1.0 */ public abstract class AbstractMessage { private IMessage message; public AbstractMessage(IMessage message) { this.message = message; } public void send(String content, String toUser){ message.send(content,toUser); } }
【4】抽象消息修正
package com.wf.bridge.demo.message; /** * @ClassName NormalMessage * @Description 普通消息 * @Author wf * @Date 2020/6/10 16:50 * @Version 1.0 */ public class NormalMessage extends AbstractMessage { public NormalMessage(IMessage message) { super(message); } } package com.wf.bridge.demo.message; import javax.sound.midi.Soundbank; /** * @ClassName UrgencyMessage * @Description 加急消息 * @Author wf * @Date 2020/6/10 16:47 * @Version 1.0 */ public class UrgencyMessage extends AbstractMessage { public UrgencyMessage(IMessage message) { super(message); } @Override public void send(String content, String toUser) { content += "[加急]"; super.send(content, toUser); watch(); } public void watch(){ System.out.println("还需要做进度跟踪"); } }
【5】测试类
package com.wf.bridge.demo.message; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/6/10 16:51 * @Version 1.0 */ public class Test { public static void main(String[] args) { IMessage message = new SmsMessage(); AbstractMessage abstractMessage = new NormalMessage(message); abstractMessage.send("加班申请","王总"); //申请一直没回,比较着急了,需要加急 message = new EmailMessage(); abstractMessage = new UrgencyMessage(message); abstractMessage.send("加班申请","王总"); } }
测试结果如下:
【6】系统类图
8.2.3.桥接模式在源码中应用
8.2.3.1.jdbc连接
java中连接数据库,底层通过jdbc来实现。
java只是提供了数据库连接的抽象,具体实现由数据库厂商来实现【如:mysql,oracle...】
所以,这里就用到了桥接模式。
1.测试类
package com.wf.bridge.demo.jdbc; import lombok.extern.slf4j.Slf4j; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; /** * @ClassName Test * @Description TODO * @Author wf * @Date 2020/6/10 17:03 * @Version 1.0 */ @Slf4j public class Test { public static void main(String[] args) { try { //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接Connection实例 //主机号:端口号/数据库名 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test3","root","root"); Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("select * from dsg_red_list_info"); System.out.println(rs); }catch (Exception e){ log.error("数据库操作失败:{}",e.getMessage()); } } }
2.桥接模式应用
package com.mysql.jdbc; import java.sql.DriverManager; import java.sql.SQLException; public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } }
为什么要实现这个java.sql.Driver呢?
在测试类,通过Class.forName找到Driver之后,首先,会加载静态块。
由DriverManager封装mysql的Driver实例,并存储到list中。
第二步,由DriverManager获取Connection实例。
DriverManage起到桥梁作用。将java的Driver,Connection和数据库厂商建立建立连接。
8.2.4.桥接模式的总结
8.2.4.1.优,缺点总结
优点:
》分离抽象部分及其具体实现部分
》提高了系统的扩展性
》符合开闭原则
》符合合成复用原则
缺点:
1.增加系统的理解与设计难度
2.需要正确地识别系统中两个独立变化的维度
8.2.4.2.桥接模式相关设计模式
1.桥接模式与组合模式
桥接模式:关注组合引用
组合模式:关注共同功能,根据主线组合
2.桥接模式与适配器模式
适配器:根据目标类,进行扩展,提高灵活性
桥接模式:必须有约定前提,并且严格按照约定实现。