设计模式怎么玩-代理模式

先来看看几个场景,比如刷网课,我们都要自己刷对吧,一节节看,还有单元测试啥的,浪费时间又学不到自己感兴趣的东西,或者玩游戏,需要升级,但是又常常充满罪恶感,可惜自己没有好好学习。那我们用代码表示一下就是这样子。
一个抽象主题接口,用来定义我们要做的事情的一个抽象接口:

package com.ongbo.ProxyPattern;

/**
 * 抽象主题接口角色
 * */
public interface SubObject {
    /**
     * 正常代理的请求方法
     */
    public void request();
    /**
     * 本篇博客用例使用的代理方法
     * 平常开发不需要
     * */
    public void login(String user,String password);
    public void kill();
    public void upgrade();
}

真实主题类,用来实现抽象主题的具体操作类

package com.ongbo.ProxyPattern;

/**
 * 真实主题
 * 需要实现抽象主题接口
 * */
public class RealSubObject implements SubObject{
    //保存用户名
    private String name = "";
    public RealSubObject(String name){
        this.name = name;
    }

    public void request() {

    }

    public void login(String user, String password) {
        System.out.println("用户"+user+"的用户"+this.name+"登陆了");
    }

    public void kill() {
        System.out.println(this.name+"杀了一个怪物");
    }

    public void upgrade() {
        System.out.println(this.name+"升级了");
    }
}

Client类,代表我自己去操作:

package com.ongbo.ProxyPattern;


public class Client_demo1 {
    public static void main(String[] args) {
        SubObject gameuser = new RealSubObject("ongbo");
        gameuser.login("xxxx","xxxxx");
        gameuser.kill();
        gameuser.upgrade();
    }
}

这上面就是一个游戏,自己作死的打,但是就是菜,打不上去,没有成就感,还耽误了自己的学习。这时候可能就会像干脆让别人代替我玩算啦。这时候就有了代理模式
代理模式即委托,代理,就是相让别人代替我做一件事情,我似乎只要发一种指令,来减少我和那件事情的耦合,比如,我们在学校有网课,可以花钱给别人代刷网课,或者你玩游戏很菜,于是让别人玩自己的号码来提神等级这些都是代理模式的一种体现。

代理模式中的角色

SubObjec抽象主题角色

声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,而Client常常需要根据抽象主题进行编程

Proxy代理主题角色

它包含了对真实主题的引用,从而可以在任何时候操作真实主题 对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代 真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实 主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所 引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中 的操作。

RealObject真实主题

它定义了代理角色所代表的真实对象,在真实主题角色中 实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操 作。
在这里插入图片描述


代理模式简单实现

抽象主题
package com.ongbo.ProxyPattern;

/**
 * 抽象主题接口角色
 * */
public interface SubObject {
    /**
     * 正常代理的请求方法
     */
    public void request();
    /**
     * 本篇博客用例使用的代理方法
     * 平常开发不需要
     * */
    public void login(String user,String password);
    public void kill();
    public void upgrade();
}

代理主题角色
package com.ongbo.ProxyPattern;
/**
 * 代理主题角色
 * */
public class ObjectProxy implements SubObject {
    /** 保存一个真实主题角色,在构造方法传入关联真实主题角色*/
    private SubObject subObject = null;
    public ObjectProxy(SubObject subObject){
        this.subObject = subObject;
    }

    /** 下面就用上面关联的真实主题角色对象来操控访问 */
    public void request() {}
    public void login(String user, String password) {
        this.subObject.login(user,password);
    }
    public void kill() {
        this.subObject.kill();
    }
    public void upgrade() {
        this.subObject.upgrade();
    }
}

真实主题角色
package com.ongbo.ProxyPattern;

/**
 * 真实主题
 * 需要实现抽象主题接口
 * */
public class RealSubObject implements SubObject{
    //保存用户名
    private String name = "";
    public RealSubObject(String name){
        this.name = name;
    }

    public void request() {

    }

    public void login(String user, String password) {
        System.out.println("用户"+user+"的用户"+this.name+"登陆了");
    }

    public void kill() {
        System.out.println(this.name+"杀了一个怪物");
    }

    public void upgrade() {
        System.out.println(this.name+"升级了");
    }
}

Client实现
package com.ongbo.ProxyPattern;

public class Client_demo2 {
    public static void main(String[] args) {
        //定义一个游戏上瘾的玩家,或者一个爱学习的但是要刷客的学生,即真实主题对象
        SubObject realObject = new RealSubObject("ongbo");
        //然后创建一个代理主题对象,并且将其与真实主题对象相关联
        SubObject ProxyObject = new ObjectProxy(realObject);

        /**接下来全部使用代理主题对象去操作*/
        ProxyObject.login("xxxx","xxx");
        ProxyObject.kill();
        ProxyObject.upgrade();
    }
}

好了,到现在为止,至少已经有人替你干活了,你就不用自己去沉迷游戏,或者还要一边学习,一边刷网课,这样就可以减轻自己的负担,让自己去做其他事情,而且不用那么多复杂的逻辑了。

上面代码看到了,在看到Client的时候,你会看见,不还是要在本地弄一个真实主题对象吗,我Client不还是要管理这个对象吗,这时候就可以修改一下,在真实主题内关联抽象主题,来看看自己创建没有,而我们把真是主题角色的创建推移到代理主题的构造方法,在Client上就只要传入一个name即可,而不用先创建一个真实主题对象再传入
Client改动

//原版
SubObject ProxyObject = new ObjectProxy(realObject);
/** 现版*/
SubObject ProxyObject = new ObjectProxy("ongbo");
//原版
public ObjectProxy(SubObject subObject){
        this.subObject = subObject;        
    }
//现版
    public ObjectProxy(String name){
        SubObject realObject = new RealSubObject(name);
        this.subObject = realObject;
    }

而在真实主题角色中,也可以自己关联代理主题角色
这样子,我们就可以将真实主题和Client屏蔽了,Client不用管真是主体的实际是谁,只知道名字,也不需要进行更复杂的操作

动态代理

有动态代理就有静态代理,上面的都是静态代理,即都要使用具体的Proxy类去实现抽象主题接口,从而才能够使Client间接使用真实主题。但是静态代理有缺点的,就是对于每个逻辑,可能都要去实现一个代理类,从而导致类爆炸,而动态代理就回去解决这个问题
在这里插入图片描述

package com.ongbo.ProxyPattern;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client_demo3 {
    public static void main(String[] args) {
        /**创建一个真是主题对象*/
        SubObject realObject = new RealSubObject("ongbo");
        /**使用JDK中的InvocationHandler,去实现这个接口,实现类NormalHandler*/
        InvocationHandler invocationHandler = new NormalHandler(realObject);
        /**获得真实主题类的ClassLoader*/
        ClassLoader cl = realObject.getClass().getClassLoader();
        /**使用Proxy.newProxyInstance去构建一个抽象主题的代理*/
        /**参数说明:
         * 第一个参数:ClassLoader,即我们真实主题对象的ClassLoader就行
         * 第二个参数:我们真实主题类实现的接口
         * 第三个参数:我们上面实现的InvocationHandler
         * */
        SubObject newProxyInstance = (SubObject)Proxy.newProxyInstance(cl, realObject.getClass().getInterfaces(), invocationHandler);
        /**调用相应的方法即可*/
        newProxyInstance.login("xx","xxx");
        newProxyInstance.kill();
        newProxyInstance.upgrade();
    }
}

那来看看那个InvocationHandler的实现类NormalHandler

package com.ongbo.ProxyPattern;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class NormalHandler implements InvocationHandler {
    /**这里是自己写的逻辑而已,引入的是真实主题的关联*/
    private Object obj = null;
    Class cls = null;
    public NormalHandler(Object obj){
        this.obj = obj;
    }
    /**这是实现Invocationhandler必须实现的方法
     * 可以看到里面使用了反射,来调用方法了。
     * */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(this.obj,args);
        return result;
    }
}

Proxy类源码解析
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{
        //检查这个Handler是不是null,会抛出NullPointExeception
        Objects.requireNonNull(h);
        //克隆我们要生成代理类的所有接口,即这个接口数组
        final Class<?>[] intfs = interfaces.clone();
        //检查权限
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 查找或者生成一个特定的代理类对象
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * 检查权限,并且调用这个代理类的参数为Handler的构造器,将其实例化,然后强制修改权限
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //利用构造器,返回代理类对象
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }



/**
     * 生成一个代理类,生成之前必须检查权限
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        //如果这个类加载已经实现了这个接口的代理类,那么就会返回这个缓存,否则就直接通过ProxyClassFactory工厂类创建这个对象
        return proxyClassCache.get(loader, interfaces);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小满锅lock

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值