设计模式-代理模式-以购房中介为例

超级链接: Java常用设计模式的实例学习系列-绪论

参考:《HeadFirst设计模式》


1.关于代理模式

代理模式是一种结构型模式。

代理模式:为其他对象提供一个代理以控制对这个对象的访问。

本文以购房中介为场景来学习代理模式

  • 购房者可以直接找房主买房。如此做较累,因为买房之前要多次房屋筛选,买房之后要签订合同等等。
  • 购房者可以找房屋中介买房,购房者只需进行少量看房即可,中介将代劳筛选房屋和签订合同等事宜。
  • 房屋中介其实最终还是通过房主完成买房。

2.房主

房子最终属于房主,首先我们先定义房主接口和房主类。

被代理对象:接口:IHouseOwner

/**
 * <p>房主接口</P>
 *
 * @author hanchao
 */
public interface IHouseOwner {
    /**
     * 房屋交易
     */
    void tradeHouse();
}

被代理对象:类:HouseOwner

/**
 * <p>房主</P>
 *
 * @author hanchao
 */
@Slf4j
public class HouseOwner implements IHouseOwner {
    /**
     * 房屋交易
     */
    @Override
    public void tradeHouse() {
        log.info("房屋交易");
    }
}

3.无代理:直接买房

/**
 * <p>购房者</P>
 *
 * @author hanchao
 */
@Slf4j
public class HouseBuyer {
    /**
     * 直接找房主购房
     */
    public void buyHouseDirectly() {
        log.info("购房者花费大量时间用于筛选房屋...最终选定了一间房。");
        new HouseOwner().tradeHouse();
        log.info("购房者与房主签订售房合同。");
    }
}

直接买房是在太麻烦了,因为购房者花费大量时间用于筛选房屋,在决定买房之后,购房者还需与房主签订一系列的售房合同。

为了省事省心,购房者决定通过售房中介进行买房。

4.静态代理

4.1.静态代理:代理对象:类

这种方式,通过定义售房中介,直接代理房主类进行售房。

/**
 * <p>房屋中介:静态代理-代理对象是类</P>
 *
 * @author hanchao
 */
@Slf4j
public class HouseAgentByStaticForClass {

    /**
     * 被代理的对象:房主
     */
    private HouseOwner owner;

    public HouseAgentByStaticForClass(HouseOwner owner) {
        this.owner = owner;
    }

    /**
     * 房屋交易
     */
    public void sellHouse() {
        //前置操作
        log.info("中介替购房者完成筛选房屋...最终选定了一间房。");

        //售房
        owner.tradeHouse();

        //后置操作
        log.info("中介替购房者完成与房主签订的售房合同。");
    }
}

下面是通过中介买房的流程。

/**
 * <p>购房者</P>
 *
 * @author hanchao
 */
@Slf4j
public class HouseBuyer {
    /**
     * 找中介买房:静态代理-代理对象:类
     */
    public void buyHouseByStaticProxyForClass() {
        //被代理对象:房主类
        HouseOwner owner = new HouseOwner();

        //代理对象
        HouseAgentByStaticForClass houseAgent = new HouseAgentByStaticForClass(owner);

        //买房
        houseAgent.sellHouse();
    }
}

4.2.静态代理:代理对象:接口

这种方式,通过定义售房中介,直接代理房主接口进行售房。

/**
 * <p>房屋中介:静态代理-代理对象是接口</P>
 *
 * @author hanchao
 */
@Slf4j
public class HouseAgentByStaticForInterface implements IHouseOwner {

    /**
     * 被代理的对象:房主
     */
    private IHouseOwner owner;

    public HouseAgentByStaticForInterface(IHouseOwner owner) {
        this.owner = owner;
    }

    /**
     * 房屋交易
     */
    @Override
    public void tradeHouse() {
        //前置操作
        log.info("中介替购房者完成筛选房屋...最终选定了一间房。");

        //售房
        owner.tradeHouse();

        //后置操作
        log.info("中介替购房者完成与房主签订的售房合同。");
    }
}

下面是通过中介买房的流程。

/**
 * <p>购房者</P>
 *
 * @author hanchao
 */
@Slf4j
public class HouseBuyer {
    /**
     * 找中介买房:静态代理-代理对象:接口
     */
    public void buyHouseByStaticProxyForInterface() {
        //被代理对象:房主接口
        IHouseOwner iHouseOwner = new HouseOwner();

        //定义代理对象
        IHouseOwner houseAgent = new HouseAgentByStaticForInterface(iHouseOwner);

        //买房
        houseAgent.tradeHouse();
    }
}

4.3.静态代理的不足

可维护性差:

  • 被代理对象与代理类一一对应,如果新增被代理对象,则需要新增代理类。
  • 被代理方法与代理方法一一对应,如果新增被代理方法,则需要新增代理方法。

5.动态代理

5.1.JDK动态代理:代理对象:接口

自定义调用处理器:实现InvocationHandler:HouseAgentByDynamicForJdk

/**
 * <p>动态代理:房屋中介</P>
 *
 * @author hanchao
 */
@Slf4j
public class HouseAgentByDynamicForJdk implements InvocationHandler {
    /**
     * 被代理的对象:房主
     */
    private IHouseOwner owner;

    public HouseAgentByDynamicForJdk(IHouseOwner owner) {
        this.owner = owner;
    }

    /**
     * 对售房进行代理
     *
     * @param proxy  代理对象实例
     * @param method 被代理的方法
     * @param args   参数
     * @return 返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //前置操作
        log.info("中介替购房者完成筛选房屋...最终选定了一间房。");

        //售房
        Object result = method.invoke(owner, args);

        //后置操作
        log.info("中介替购房者完成与房主签订的售房合同。");

        return result;
    }
}

JDK动态代理实现步骤:

/**
 * <p>购房者</P>
 *
 * @author hanchao
 */
@Slf4j
public class HouseBuyer {
		/**
     * 找中介买房:JDK动态代理-代理对象:接口
     */
    public void buyHouseByDynamicProxyForJdk() {
        //被代理的对象:房主接口
        IHouseOwner owner = new HouseOwner();

        //被代理对象的接口加载器
        ClassLoader classLoader = IHouseOwner.class.getClassLoader();

        //被代理的接口的类型
        Class[] classes = {IHouseOwner.class};

        //代理时的调用处理器:房租中介
        InvocationHandler proxyHandler = new HouseAgentByDynamicForJdk(owner);

        //代理对象
        IHouseOwner proxy = (IHouseOwner) Proxy.newProxyInstance(classLoader, classes, proxyHandler);

        //代理售房
        proxy.tradeHouse();
    }
}

JDK通过java.lang.reflect.Proxy类能够实现动态代理,其主要流程如下:

  • 实现InvocationHandler接口,自定义自己需要的调用处理器:invocationHandler
  • 获取被代理对象的接口加载器:classLoader
  • 获取被代理的接口的类型:clazz[]
  • 通过java.lang.reflect.Proxy的静态方法newProxyInstance(),以invocationHandlerclassLoaderclazz[]为参数,生成代理对象。
  • 调用代理对象的代理方法。

**注意:**根据上述流程,可以确定JDK动态代理针对的是接口,而非

如何实现对类的动态代理呢?这就需要CGLib了。

5.2.CGLIB动态代理:代理对象:类

自定义方法解释器:实现MethodInterceptor:HouseAgentByDynamicForJdk

/**
 * <p></P>
 *
 * @author hanchao
 */
@Slf4j
public class HouseAgentByDynamicForCglib implements MethodInterceptor {
    /**
     * 被代理的对象:房主
     */
    private HouseOwner owner;

    public HouseAgentByDynamicForCglib(HouseOwner owner) {
        this.owner = owner;
    }

    /**
     * 对售房进行代理
     *
     * @param obj         代理对象实例
     * @param method      被代理的方法
     * @param args        参数
     * @param methodProxy 方法代理
     * @return 返回值
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws InvocationTargetException, IllegalAccessException {
        //前置操作
        log.info("中介替购房者完成筛选房屋...最终选定了一间房。");

        //售房
        Object result = method.invoke(owner, args);

        //后置操作
        log.info("中介替购房者完成与房主签订的售房合同。");

        return result;
    }
}

CGLIB动态代理实现步骤:

/**
 * <p>购房者</P>
 *
 * @author hanchao
 */
@Slf4j
public class HouseBuyer {
    /**
     * 找中介买房:CGLIB动态代理-代理对象:类
     */
    public void buyHouseByDynamicProxyForCglib() {
        //被代理的对象:房主
        HouseOwner owner = new HouseOwner();

        //CGLIB增强的代理类
        Enhancer enhancer = new Enhancer();

        //被代理对象的类型
        enhancer.setSuperclass(HouseOwner.class);

        //定义代理时的方法解释器
        MethodInterceptor methodInterceptor = new HouseAgentByDynamicForCglib(owner);

        //以回调方式设置代理行为
        enhancer.setCallback(methodInterceptor);

        //创建代理对象
        HouseOwner roomowner = (HouseOwner) enhancer.create();

        //代理售房
        roomowner.tradeHouse();
    }
}

JDK通过org.springframework.cglib.proxy.Enhancer类能够实现动态代理,其主要流程如下:

  • 实现MethodInterceptor接口,自定义自己需要的方法解释器:methodInterceptor
  • 创建CGLIB增强型代理类:enhancer
  • enhancer设置被代理对象的类型:clazz
  • methodInterceptor为参数,为enhancer设置回调方法。
  • 调用Enhancer#create()方法创建代理对象。
  • 调用代理对象的代理方法。

注意:CGLIB动态代理针对的是

使用CGLIB可以直接引用其依赖,也可以使用springframework的内置CGLIB。

CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

6.实际应用场景

  • Struts2中的拦截器Interceptor
  • Spring中的AOP
  • AspectJ的实现。

7.总结

最后以UML类图来总结本文的售房中介场景以及代理模式
在这里插入图片描述

相关推荐
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页