结构型模式描述如何将类或对象按某种布局组成更大的结构。
继承机制
来组织接口和类,组合或聚合
来组合对象。结构型模式分为以下7种:
代理(Proxy)模式:客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
桥接(Bridge)模式:将抽象与实现分离
,使他们可以独立的变化。用组合关系代替继承关系来实现,从而降低了抽象和实现这2个可变维度的耦合度。
装饰(Decorator)模式:动态的给对象增加一些职责
,即增加其额外的功能。
外观(Facade)模式:为多个复杂的子系统提供一个一致的接口
, 使这些子系统更加容易被访问。
享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度 对象的复用
。
组合(Composite)模式:将对象组合成树状层次结构
,使用户对单个对象和组合对象具有一致的访问性。
注:以上7种结构型模式,除了适配器模式分为类结构型模式和对象结构型模式2种,其他的全部属于对象结构型模式。
优点:
(1)中介作用,保护目标对象的作用;
(2)代理对象可以扩展目标对象的功能;
(3)将客户端与目标对象分离,降低了系统的耦合度。
缺点:
(1)请求处理速度变慢;
(2)增加了系统的复杂度。
结构
(1)抽象主题(Subject)类:通过接口或抽象类
声明真实主题和代理对象实现的业务方法。
(2)真实主题(RealSubject)类:实现了抽象主题中的具体业务
,是代理对象所代表的真实对象,是我们最终要引用的对象。
(3)代理(Proxy)类:提供了与真实主题相同的接口,其内部含 有对真实主题的引用,它可以访问或控制或扩展真实主题的功能
。
代码如下:
测试类
public class ProxyTest {
public static void main(String[] args) {
// 创建代理对象
Proxy proxy = new Proxy();
// 通过代理对象调用真实对象
proxy.Request();
}
}
抽象主题
interface Subject
{
// 真实主题和代理对象实现的业务方法。
void Request();
}
真实主题
// 真实主题,实现抽象主题
class RealSubject implements Subject
{
public void Request()
{
System.out.println("访问真实主题方法...");
}
}
代理对象
class Proxy implements Subject
{
// 要代理的真实对象
private RealSubject realSubject;
// 业务方法
public void Request()
{
if (realSubject == null)
{
realSubject = new RealSubject();
}
// 对代理对象的增强
preRequest();
realSubject.Request();
postRequest();
}
// 增强方法
public void preRequest()
{
System.out.println("访问真实主题之前的预处理。");
}
public void postRequest()
{
System.out.println("访问真实主题之后的后续处理。");
}
}
代码如下:
测试类
public class WySpecialtyProxy {
public static void main(String[] args) {
// 创建韶关代理公司
SgProxy proxy = new SgProxy();
// 实现具体业务
proxy.display();
}
}
抽象主题:特产
interface Specialty
{
// 具体业务的方法
void display();
}
真实主题:婺源特产
class WySpecialty implements Specialty
{
private static final long serialVersionUID = 1L;
public WySpecialty()
{
}
public void display()
{
System.out.println("访问婺源特产。");
}
}
代理类:韶关代理
class SgProxy implements Specialty
{
// 要代理的真实对象
private WySpecialty realSubject = new WySpecialty();
public void display()
{
preRequest();
realSubject.display();
postRequest();
}
public void preRequest()
{
System.out.println("韶关代理婺源特产开始。");
}
public void postRequest()
{
System.out.println("韶关代理婺源特产结束。");
}
}
这种方式通常是为了隐藏目标对象存在于不同地址空间的事实, 方便客户端访问。
例如用户申请某些网盘空间时,会在用户的文件系统中建立一 个虚拟的硬盘,用户访问它实际访问的是网盘空间。
用于要创建的目标对象开销很大时。
比如下载 一幅很大的图像需要很长时间,短时间无法完成,这时可 以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
这种方式通常用于控制不同种类客户对真实对象的访问权限。
主要用于当调用目标对象时,代理附加一些额外的处理功能。
比如增加计算真实对象的引用次数的功能,这样当该对象没有引用时,就可以自 动释放它。
指为了提高系统的性能,延迟对目标的加载。
例如, Hibernate中就存在属性的延迟加载和关联表的延时加载。
前面介绍是静态代理模式,存在两个缺点:
Java中提供了一个动态代理类Proxy,Proxy并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法(newProxyInstance方法)来获取代理对象。
例:动态代理实现火车站卖票
卖票接口
public interface SellTickets {
void sell();
}
火车站 :火车站具有卖票功能,所以需要实现SellTickets接口
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站卖票");
}
}
代理工厂,用来创建代理对象
public class ProxyFactory {
// 要代理的对象
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
//使用Proxy获取代理对象
/*
newProxyInstance()方法参数说明:
ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可
Class>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
InvocationHandler h : 代理对象的调用处理程序
*/
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
/*
InvocationHandler中invoke方法参数说明:
proxy : 代理对象
method : 对应于在代理对象上调用的接口方法的 Method 实例
args : 代理对象调用接口方法时传递的实际参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
//执行真实对象
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
测试类
public class Client {
public static void main(String[] args) {
//获取代理对象
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
注:ProxyFactory不是代理模式中所说的代理类,而代理类是程序在运行过程中动态的在内存中生成的类
如果没有定义SellTickets
接口,只定义了TrainStation
(火车站类)。很显然JDK代理是无法使用了,因为JDK动态代理要求必须定义接口,对接口进行代理。
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。
CGLIB是第三方提供的包,所以需要引入jar包的坐标:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
代码如下:
火车站
public class TrainStation {
public void sell() {
System.out.println("火车站卖票");
}
}
代理工厂
public class ProxyFactory implements MethodInterceptor {
private TrainStation target = new TrainStation();
public TrainStation getProxyObject() {
//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
Enhancer enhancer =new Enhancer();
//设置父类的字节码对象
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
TrainStation obj = (TrainStation) enhancer.create();
return obj;
}
/*
intercept方法参数说明:
o : 代理对象
method : 真实对象中的方法的Method实例
args : 实际参数
methodProxy :代理对象中的方法的method实例
*/
public TrainStation intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("代理点收取一些服务费用(CGLIB动态代理方式)");
TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args);
return result;
}
}
测试类
public class Client {
public static void main(String[] args) {
//创建代理工厂对象
ProxyFactory factory = new ProxyFactory();
//获取代理对象
TrainStation proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}