软件设计模式--第三章 结构性模式--代理模式

目录

  • 第二章 结构型模式
    • 1、结构型模式概述
    • 2、代理模式
      • (1)模式的定义与特点
      • (2)模式的结构与实现
      • (3)应用场景
      • (4)扩展(动态代理)
        • (1)JDK动态代理
        • (2)CGLIB动态代理


第二章 结构型模式

1、结构型模式概述

结构型模式描述如何将类或对象按某种布局组成更大的结构。

  • 类结构型模式:采用继承机制来组织接口和类,
  • 对象结构型模式:采用组合或聚合来组合对象。
  • 由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。

结构型模式分为以下7种:

  1. 代理(Proxy)模式:客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。

  2. 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

  3. 桥接(Bridge)模式:将抽象与实现分离,使他们可以独立的变化。用组合关系代替继承关系来实现,从而降低了抽象和实现这2个可变维度的耦合度。

  4. 装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。

  5. 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口, 使这些子系统更加容易被访问。

  6. 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度 对象的复用

  7. 组合(Composite)模式将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。

注:以上7种结构型模式,除了适配器模式分为类结构型模式和对象结构型模式2种,其他的全部属于对象结构型模式。


2、代理模式

(1)模式的定义与特点

  1. 定义:给某对象提供一个代理以控制对该对象的访问。访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介

软件设计模式--第三章 结构性模式--代理模式_第1张图片

  1. 优点:
    (1)中介作用,保护目标对象的作用;
    (2)代理对象可以扩展目标对象的功能;
    (3)将客户端与目标对象分离,降低了系统的耦合度。

  2. 缺点:
    (1)请求处理速度变慢;
    (2)增加了系统的复杂度。

(2)模式的结构与实现

  1. 结构
    (1)抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
    (2)真实主题(RealSubject)类实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是我们最终要引用的对象。
    (3)代理(Proxy)类:提供了与真实主题相同的接口,其内部含 有对真实主题的引用,它可以访问或控制或扩展真实主题的功能

  2. 结构图如下:
    软件设计模式--第三章 结构性模式--代理模式_第2张图片
    软件设计模式--第三章 结构性模式--代理模式_第3张图片

代码如下:

测试类

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("访问真实主题之后的后续处理。");
    }
}
  1. 实现
    例: 韶关“天街e角”公司是一家婺源特产公司的代售公 司,用代理模式实现。

代码如下:
测试类

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("韶关代理婺源特产结束。");
    }
}

(3)应用场景

  1. 远程代理

这种方式通常是为了隐藏目标对象存在于不同地址空间的事实, 方便客户端访问。
例如用户申请某些网盘空间时,会在用户的文件系统中建立一 个虚拟的硬盘,用户访问它实际访问的是网盘空间。

  1. 虚拟代理

用于要创建的目标对象开销很大时。
比如下载 一幅很大的图像需要很长时间,短时间无法完成,这时可 以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。

  1. 安全代理

这种方式通常用于控制不同种类客户对真实对象的访问权限。

  1. 智能指引

主要用于当调用目标对象时,代理附加一些额外的处理功能。
比如增加计算真实对象的引用次数的功能,这样当该对象没有引用时,就可以自 动释放它。

  1. 延迟加载

指为了提高系统的性能,延迟对目标的加载。
例如, Hibernate中就存在属性的延迟加载和关联表的延时加载。

(4)扩展(动态代理)

前面介绍是静态代理模式,存在两个缺点:

  • 真实主题与代理主题一一对应,增加真实主题也要增加代理;
  • 设计代理以前真实主题必须事先存在,不太灵活。

(1)JDK动态代理

Java中提供了一个动态代理类Proxy,Proxy并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法(newProxyInstance方法)来获取代理对象。

例:动态代理实现火车站卖票

软件设计模式--第三章 结构性模式--代理模式_第4张图片

卖票接口

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不是代理模式中所说的代理类,而代理类是程序在运行过程中动态的在内存中生成的类

(2)CGLIB动态代理

如果没有定义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();
    }
}

你可能感兴趣的:(软件设计模式-学习笔记,java,软件设计模式,结构型模式,代理模式)