目录标题
前言
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。
结构型模式分为以下 7 种:
- 代理模式
- 适配器模式
- 装饰者模式
- 桥接模式
- 外观模式
- 组合模式
- 享元模式
代理模式
代理模式
概述
1. 代理
代购, 中介,换ip,商家等等。
为什么要找中介 ?
1. 中介是专业的, 方便
2. 家长现在不能自己去找学校。 家长没有能力访问学校。 或者美国学校不接收个人来访。
买东西都是商家卖, 商家是某个商品的代理, 你个人买东西, 肯定不会让你接触到厂家的。
在开发中也会有这样的情况, 你有a类, 本来是调用c类的方法, 完成某个功能。 但是c不让a调用。
a -----不能调用 c的方法。
在a 和 c 直接 创建一个 b 代理, c让b访问。
a --访问b---访问c
实际的例子: 登录,注册有验证码, 验证码是手机短信。
中国移动, 联通能发短信。
中国移动, 联通能有子公司,或者关联公司,他们面向社会提供短信的发送功能
张三项目发送短信----子公司,或者关联公司-----中国移动, 联通
代理的作用
功能增强: 在你原有的功能上,增加了额外的功能。
控制访问: 代理类不让你访问目标,例如商家不让用户访问厂家。
结构
接口:表示功能,厂家,商家都要完成的功能
目标类: 金士顿厂家, 不接受用户的单独购买
代理类:商家
1. 静态代理
实现步骤:
- 创建一个接口,定义卖u盘的方法, 表示你的厂家和商家做的事情。
- 创建厂家类,实现1步骤的接口
- 创建商家,就是代理,也需要实现1步骤中的接口。
- 创建客户端类,调用商家的方法买一个u盘。
接口
public interface UsbSell {
//返回值表示一个u盘的价格。
float sell(int count);
}
目标类: 金士顿厂家, 不接受用户的单独购买。
public class UsbKingFactory implements UsbSell {
@Override
public float sell(int count) {
System.out.println("目标类中的方法调用 , UsbKingFactory 中的sell ");
//一个128G的u盘是 85元。
return 85;
}
}
代理类
public class JD implements UsbSell {
//声明 商家代理的厂家具体是谁
private UsbSell factory = new UsbKingFactory();
@Override
public float sell(int count) {
//向厂家发送订单,告诉厂家,我买了u盘,厂家发货
float price = factory.sell(1);
//商家 需要加价, 也就是代理要增加价格。
price += 40;//增强功能,代理类在完成目标类方法调用后,增强了功能。
System.out.println("京东送您100京东豆!");
return price;
}
}
客户端【用户】
public class Customer {
public static void main(String[] args) {
JD j = new JD();
float price = j.sell(1);
System.out.println(price);
}
}
总结
- 当接口中功能增加了, 或者修改了,会影响众多的实现类,厂家类,代理都需要修改。影响比较多。
- 代理类过多:此时我们只有一家JD卖卖金士顿u盘,当在来一家TaoBao也要卖金士顿u盘时,就要再写一个代理类,这要是有100家企业要买u盘就要写100个代理类,会显得代理类数量过多。并且当目标类【u盘厂家】多起来了之后,代理类都是成倍数上涨的!
2. JDK动态代理
实现步骤:
- 创建一个接口,定义卖u盘的方法, 表示你的厂家和商家做的事情。
- 创建厂家类,实现1步骤的接口
- 创建类实现接口InvocationHandler重写invoke()方法, 把原来静态代理中代理类要完成的功能写在这。
- 创建客户端类,调用商家的方法买一个u盘
接口【表示功能的,厂家,商家都要完成的功能】
public interface UsbSell {
float sell(int count);
}
目标类 【厂家】
public class UsbKingFactory implements UsbSell {
@Override
public float sell(int count) {
System.out.println("目标类中的方法调用 , UsbKingFactory 中的sell ");
return 85;
}
}
创建类实现接口InvocationHandler:完成代理类要做的功能(1.调用目标方法,2.功能增强)
//必须实现InvocationHandler接口,完成代理类要做的功能(1.调用目标方法,2.功能增强)
public class MySellHandler implements InvocationHandler {
//传入进来的对象
private Object target;
//动态代理:目标对象是活动的,不是固定的,需要传入进来。
//传入是谁,就给谁创建代理。
public MySellHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
//float price = factory.sell(1);
res = method.invoke(target, args);//执行目标方法
//商家 需要加价, 也就是代理要增加价格。
//price = price + 25; //增强功能,代理类在完成目标类方法调用后,增强了功能。
if (res != null){
Float price = (Float) res;
price += 25;
res = price;
}
//在目标类的方法调用后,你做的其它功能,都是增强的意思。
System.out.println("淘宝送您优惠券一张!");
return res;
}
}
客户端【用户】
public class Customer {
public static void main(String[] args) {
//创建代理对象,使用Proxy
//1. 创建目标对象
UsbSell factory = new UsbKingFactory();
//2.创建InvocationHandler对象
InvocationHandler handler = new MySellHandler(factory);
//3.创建代理对象
UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), handler);
//com.sun.proxy.$Proxy0 : 这是jdk动态代理创建的对象类型。
System.out.println(proxy.getClass().getName());
//4.通过代理执行方法
float price = proxy.sell(1);//执行的是MySellHandler类里面的invoke方法
System.out.println(price);
}
}
动态代理执行过程
程序执行到proxy.sell(1)语句会调用InvocationHandler实现类的invoke()方法,然后一次执行下来,最终返回值返回给方法调用处proxy.sell(1).
动态代理分析
动态代理是一种创建java对象的能力,让你不用创建JD类,就能创建代理类对象。
动态代理使用java反射包中的类和接口实现动态代理的功能。
动态代理使用到的三个类:反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method , Proxy
InvocationHandler【接口】
InvocationHandler 接口(调用处理器):就一个方法invoke()
invoke():表示代理对象要执行的功能代码。你的代理类要完成的功能就写在invoke()方法中。
Method
Proxy.newProxyInstance() 方法【静态方法】-- 创建代理对象
创建代理对象, 等同于静态代理中的TaoBao taoBao = new TaoBao();
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
ClassLoader loader 类加载器,负责向内存中加载对象的。 使用反射获取对象的ClassLoader.【类a , a.getCalss().getClassLoader(), 目标对象的类加载器】
Class<?>[] interfaces: 接口, 目标对象实现的接口,也是反射获取的。【类a , a.getClass().getInterfaces().】
InvocationHandler h : 我们自己写的,代理类要完成的功能。
返回值:就是代理对象。
3. CGLIB动态代理
三种代理的对比
jdk代理和CGLIB代理
JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。
动态代理与静态代理
如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题
优缺点
优点:
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
代理对象可以扩展目标对象的功能;
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
缺点:
增加了系统的复杂度;
面试题
为什么不实现接口就不能用JDK代理,只能使用CGLIB代理
代理类($Proxy0)继承了Proxy类,由于java中单继承,所以当目标对象没有实现接口的话,当生成代理类的时候需要继承目标对象,又要继承Proxy类,违反了单继承,因此如果没有实现接口的目标对象只能使用CGLIB代理
来源:https://www.bilibili.com/video/BV1Np4y1z7BU?p=62&vd_source=b901ef0e9ed712b24882863596eab0ca
demo来源:https://blog.csdn.net/qq_44715943/article/details/120785432