结构型模式
- 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者则采用组合或聚合来组合对象。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。
- 结构型模式分为一下7种:
- 代理模式
- 适配器模式
- 装饰者模式
- 桥接模式
- 外观模式
- 组合模式
- 享元模式
代理模式(中介商模式)
- 概述:
代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
Java中的代理按照代理类生成实际不同又分为静态代理和动态代理。静态代理类在编译时期就生成,而动态代理类则是在Java运行时动态生成,动态代理又有JDK代理和CGLib代理。
- 结构:
代理模式分为三种角色:
- 抽象主题类(Subject):通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题类(RealSubject):实现抽象主题类中的具体业务,是代理对象所代表的真是对象,是最终要引用的对象。
- 代理类(Proxy):提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
优缺点:
- 优点:
- 代理模式在客户端与目标对象之间起到了一个中介作用和保护目标对象的作用
- 代理对象可以扩展目标对象的功能
- 代理模式能将客户端与目标对象分离,在一定程度上减低了系统的耦合性
- 缺点:
- 代理模式会导致系统中类的数量增加,在一定程度上增加了系统的复杂度
- 代理模式的性能可能会受到影响,因为代理对象需要额外的处理来转发请求
- 优点:
使用场景:
- 远程代理
2.防火墙代理 - 保护代理
静态代理
- 实例:火车站售票
- 场景:
- 火车站售票系统中,售票窗口是客户端,而售票系统是服务器端。为了保护售票系统的安全,我们可以使用代理模式来实现。
- 代码:
- 抽象主题类(Subject):
public interface SellTickets {
void sell();
} - 真实主题类(RealSubject):
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站售票");
}
} - 代理类(Proxy):
public class TrainStationProxy implements SellTickets {
//先声明对象
private TraninStation traninStation = new TraninStation();
public void sell() {
System.out.println("代理点收取一些服务费用");
traninStation.sell();
}
} - 客户端:
public class Client {
//代理模式
public static void main(String[] args) {
ProxyPoint proxyPoint = new ProxyPoint();
proxyPoint.sell();
}
}
//结果:代理点收取一些服务费用 火车站卖票
- 抽象主题类(Subject):
- 场景:
动态代理——JDK代理模式
Java中提供了一个动态代理类Proxy,Proxy并不是上面我们上述所说的代理对象类,而是提供了一个创建代理对象的静态方法(newProxyInstance方法),来获取代理对象。
实例:同样是上面的卖票案例,这次我们使用JDK动态代理模式来实现
抽象主题类(Subject):
public interface SellTickets {
void sell();
}真实主题类(RealSubject):
public class TrainStation implements SellTickets { |
- 代理主题类(ProxyFactory):
import java.lang.reflect.InvocationHandler; |
- 测试类:
|
注意:在’ProxyFactory’类中,getProxyObject()方法返回的是SellTickets接口,而不是TrainStation类。
这是因为JDK动态代理模式是基于接口的代理,而不是基于类的代理。
所以,代理对象只能实现与目标对象相同的接口,而不能继承目标对象的类。
同时代理类是程序在运行过程中动态的在内存中生成的类。上述的执行流程:
- 在测试类中通过代理对象调用sell()方法
- 根据多态的特性,执行的是代理类($Proxy0)中的sell()方法,而不是TrainStation类中的sell()方法。
- 在$Proxy0类中,sell()方法的实现是通过InvocationHandler接口的子实现类对象的invoke()方法来完成的。
- 在invoke()方法中,通过反射机制调用执行真实所属类(TraninStation)目标对象中的sell()方法。
动态代理——CGLIB代理模式
- CGLIB(Code Generation Library)是一个基于ASM的字节码生成类库(代码生成包,是第三方提供的包,所以需要时引入jar包),用于在运行时动态生成一个类。
- 在pom.xml文件中引入CGLIB的依赖:
<dependencies> |
public class TraninStation { |
import net.sf.cglib.proxy.Enhancer; |
public class Client { |
总结:
jdk动态代理基于反射实现,cglib基于继承实现,所以cglib动态代理类是目标类的的子类,而jdk动态代理类是目标对象实现的接口的子类。