[19/04/25-星期四] GOF23_结构型模式(适配器模式、代理模式)
一、引言
结构模式:核心作用就是从程序的结构上实现松耦合,从而扩大整体的类结构,用来解决更大的问题。
二、适配器模式(adapter)
生活中假设笔记本是标准的USB接口但是外置键盘是圆形接口,这时候就需要一个适配器,如下图所示。一端连接笔记本,一端连接键盘。
用途:一个如下的适配器价格低于一个新的键盘价格,键盘可以打字,现在需要打字但是买不起键盘但可以买得起适配器,这是适配器就有用途了。还比如新型的带USB的插座
也是同样的道理,也是适配器。
概念:将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原来由于接口不兼容而不能在一起工作的那些类可以在一起工作。
角色: 目标接口:客户所希望的接口。目标可以是具体的也可以是抽象的的类,也可以是接口。(如下图中电脑所需要的USB接口)
需要适配的类:需要适配的类或适配者类。(如下图中的 "键盘" 类)
适配器类:通过包装一个需要适配的对象,把原来的接口转换成目标接口。(如下图中的适配器产品)
使用场景:旧系统的升级和改造
Java.io.InputStreamReader(InputStream)
Java.io.OutputStreamReader(OutputStream)
【适配器】
/** * 相当于USB接口 */ package cn.sxt.adapter; public interface Target { void handleRequst(); } /*** * 适配器本身(一个转接器),既继承被适配类又实现Target接口(相当于USB接口),中间是2边都要熟悉,功能都得具备 * 类适配器的模式 (因为有继承,extends Adaptee) */ package cn.sxt.adapter; public class Adapter extends Adaptee implements Target{ //重写接口USB的handleRequst方法 public void handleRequst() { super.request();//super指的是Adaptee类对象 request也是它的方法 } }
【被适配对象】
/*** * 被适配的类(就是圆形接口(ps/2接口)键盘) */ package cn.sxt.adapter; public class Adaptee { public void request() { System.out.println("可以完成客户需要打字的功能"); } }
/*** * 使用对象适配器模式,使用了组合的模式,跟被适配对象整合 */ package cn.sxt.adapter; public class Adapter2 implements Target{ private Adaptee adaptee; public void handleRequst() { adaptee.request(); } public Adapter2(Adaptee adaptee) { super(); this.adaptee = adaptee; } }
【客户端】
/*** * 客户类:相当于笔记本电脑,只有USB接口 */ package cn.sxt.adapter; public class Client { public void test01(Target t) { t.handleRequst(); } public static void main(String[] args) { Client client=new Client();//一个只有USB接口的笔记本 Adaptee adpatee=new Adaptee();//一个只有ps/2接口的键盘 Target target=new Adapter();//Target 一个USB接口 client.test01(target); Target target2=new Adapter2(adpatee); client.test01(target2);//结果一样 } }
【类图】
三、代理模式(Proxy Pattern)
核心作用:通过代理,控制对对象的访问 ,可以详细控制访问某个或某类对象的方法,在调用方法前做前置处理(如面谈、签合同),
调用这个方法后做后置处理(如收尾款),即AOP的微观实现。
【AOP扩展】
AOP:Aspect Oriented Programming 面向切面编程。核心机制就是代理模式
一个代理可以代理多个明星的业务(面谈、签合同、收尾款)。核心的业务如唱歌还是要明星自己完成。如果代理的明星多的话,那么代理占据很重要的位置。
每次演唱会都有相同的流程可以将一些相同的流程交给代理去做(如面谈、签合同、收尾款)
AOP:流程好多一样,可以把一样的流程handler中的invoke方法中去处理。它的开源框架是:AspectJ
【代理模式详解】
应用场景:
安全代理:屏蔽对真实角色的访问
远程代理:通过代理类处理远程方法的调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象(如:一个文档查看软件,文档中有可能一个图片很大,在打开文件时不可能将所有图片都显示出来
可以使用代理,在需要查看图片时,用代理去打开大的图片。新浪微博9宫格,先看到时缩略图,需要看某个图片可以放大看)
开发框架中应用:
Struts2 中拦截器的实现;数据库连接池的关闭处理;Hibernate中延时加载的实现;mybatis中拦截器插件的实现;AspectJ的实现;
Spring中AOP的实现(日志拦截、声明式事务处理);web service ;RMI远程方法的调用。
分类:
静态代理 (静态定义代理类):代理类由我们自己定义
动态代理(动态生成代理类): 代理类由程序(Java工具表)生成。包括:JDK自带的动态代理、javaassist字节码操作库实现、CGLIB(Code Generation Library)、
ASM(底层使用命令,可维护性差)
1、静态代理
【接口】
/*** * 定义一个"明星"接口 */ package cn.sxt.staticProxy; public interface Star { void confer(); //面谈 void signContract();//签合同 void bookTicket();//订飞机票 void sing();//唱歌 void collectMoney();//收钱 }
【真实角色】
/*** * 真实角色:明星本身 */ package cn.sxt.staticProxy; public class RealStar implements Star{ @Override public void confer() { System.out.println("真实角色(周杰伦本人):面谈"); } @Override public void signContract() { System.out.println("真实角色(周杰伦本人):签合同"); } @Override public void bookTicket() { System.out.println("真实角色(周杰伦本人):订飞机票"); } @Override public void sing() { System.out.println("真实角色(周杰伦本人):唱歌"); } public void collectMoney() { System.out.println("真实角色(周杰伦本人):收尾款"); } }
【代理角色】
/**代理角色:经纪人 * */ package cn.sxt.staticProxy; public class ProxyStar implements Star { private Star realStar; public ProxyStar(Star realStar) { this.realStar=realStar; } @Override public void confer() { System.out.println("代理角色(经纪人):面谈"); } @Override public void signContract() { System.out.println("代理角色(经纪人):签合同"); } @Override public void bookTicket() { System.out.println("代理角色(经纪人):订飞机票"); } //代理角色什么都能做但是就是不能唱歌,需要调用真实角色去唱歌 public void sing() { realStar.sing(); } public void collectMoney() { System.out.println("代理角色(经纪人):收尾款"); } }
【客户】
/*** * 客户类 */ package cn.sxt.staticProxy; public class Client { public static void main(String[] args) { Star realStar=new RealStar(); Star proxyStar=new ProxyStar(realStar);//代理类需要传进去一个真实的角色(代理对象) proxyStar.confer(); proxyStar.signContract(); proxyStar.bookTicket(); proxyStar.sing();//代理角色(经纪人)需要调用真实角色(周杰伦)去完成唱歌的功能。客户可以只跟经纪人打交道 proxyStar.collectMoney(); } }
【类图】
2、动态代理
(1) JDK自带动态处理
java.lang.reflect.Proxy 作用:生成代理类和对象
Java.lang.reflect.Invocationhandler(处理器接口) 可以通过invoke方法实现对真实角色的代理访问,每次通过Proxy生成代理类对象时都要指定对应的处理器接口。
【接口】
/*** * 定义一个"明星"接口 */ package cn.sxt.dynamicProxy; public interface Star { void confer(); //面谈 void signContract();//签合同 void bookTicket();//订飞机票 void sing();//唱歌 void collectMoney();//收钱 }
【真实角色】
/*** * 真实角色:明星本身 */ package cn.sxt.dynamicProxy; public class RealStar implements Star{ @Override public void confer() { System.out.println("真实角色(周杰伦本人):面谈"); } @Override public void signContract() { System.out.println("真实角色(周杰伦本人):签合同"); } @Override public void bookTicket() { System.out.println("真实角色(周杰伦本人):订飞机票"); } @Override public void sing() { System.out.println("真实角色(周杰伦本人):唱歌"); } public void collectMoney() { System.out.println("真实角色(周杰伦本人):收尾款"); } }
【处理核心】
/** * */ package cn.sxt.dynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class StarHandler implements InvocationHandler { Star realsStar; public StarHandler(Star realsStar) { this.realsStar = realsStar; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("####");//代理类不管调用什么方法都会到这里集中报道 System.out.println("真正方法(唱歌)执行前:面谈、签合同、预付款、订机票"); if ( method.getName().equals("sing") ) {//如果是唱歌方法激活真实角色的唱歌方法 method.invoke(realsStar, args);//激活传入对象的 args(形参)方法 } System.out.println("真正方法(唱歌)执行后:付尾款"); return null; } }
【客户端】
/**客户类 * */ package cn.sxt.dynamicProxy; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { Star realStar=new RealStar(); StarHandler handler=new StarHandler(realStar); Star proxyStar=(Star)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] {Star.class}, handler);//返回一个代理对象 proxyStar proxyStar.confer(); proxyStar.sing(); proxyStar.collectMoney(); } }
【类图】