代理&工厂方法的技巧

前置基础:反射知识
首先jdk动态代理局限于接口是因为java只支持单继承。建议看完再点这个看。

代理的思想主要是用于接口之中,对于原有的程序接口,如果要在调用该接口对象增加新的需求,我们不能去改变接口类,所以我们应该怎么办呢?
举个栗子,对一个用于实现一个接口Eat的实现类EatInstance。

public interface Eat{
    void eat();
}


public class EatInstance implements Eat{
    @Override
    void eat(){
        System.out.println("eating");
    }
}
//------------测试方法----------
public void main(String[] args){
    Eat eating = new EatInstance();
    eatint.eat();
}

现在如果我要在做吃这个动作之前去洗手,我不能改变Eat接口,那我要加个if进行判断?这就不符合Java面向对象的思想了。那我该改变EatInstance类,在重写的eat方法里面加入输出洗手吗?要注意不是每个人都会饭前洗手的,不过思路是对的,因此给出的解决方案是新建一个实现Eat接口的类去完成。代码如下:

public interface Eat{
    void eat();
}


public class EatInstance implements Eat{
    @Override
    void eat(){
        System.out.println("eating");
    }
}


public class WashEatProxy implements Eat{
    @Override
    void eat(){
        EatInstance real = new EatInstance();//自身实现,而不由构造器传入,这也是同装饰者模式不同的地方
        wash();
        real.eat();     //保持一致性
    }

    void static wash(){
        System.out.println("washing");  
    }
}

//------------测试方法----------
public void main(String[] args){
    Eat eating = new WashEatProxy(); //注意和上文的测试方法对比,发现只有半行代码变化就可以实现
    eatint.eat();
}

这种方法也叫做静态代理,所以在新接口实现我用了Proxy这个名字。
这个方法可以最小程度的改变原有代码,仅仅通过改变接口的实现类的方式进行修改(不知道这算不算是面向接口编程。。。)。

接着,有时候对一个人来说不仅仅是饭前要洗手,上厕所前也要先洗手,吃手前也要先洗手(想不出来了编一个。。)等很多时候都要先洗手,对于各种情况(接口)前都加上洗手这个操作,笨办法就是每个都去写,都去改。于是有人就想出了动态代理的方法,具体思路就是我建立一个调用处理器(InvocationHandler)用来编写这个洗手的方法,对于某一个类,他实现了各种接口,于是在他的实例要调用接口时,凡是要用到调用处理器里面包含这个洗手的方法的,都要加上调用处理器实现的DIY功能。具体代码如下:

public interface Eat{
    void eat();
}

public interface Toilet{
    void toilet();
}

public interface EatHand{
    void eatHand();
}

public class Human implements Eat, Toilet, EatHand{
    @Override
    void eat(){
        System.out.println("Eating");
    }
    @Override
    void toilet(){
        System.out.println("Going to the toilet");
    }
    @Override
    void eatHand(){
        System.out.println("eating the hand");
    }
}

public class ActionHandler implements InvocationHandler{
    private Object instanceT;
    public ActionHandler(Object T){
        instanceT = T;
    }
    public Object invoke(Object proxy , Method method , Object[] args)
throws Throwable{
        wash();  //DIY方法
        return method.invoke(instanceT, args);//真正调用的方法,instanceT是之前传进来的具体实例,args是接口所需要的方法参数,详见反射
    }
    static void wash(){
        System.out.println("washing");  
    }
}


//------------测试方法----------
public static void main(String[] args){
    Human h = new Human();
    ActionHandler action = new ActionHandler(h);
    Object proxy = Proxy.newProxyInstance(h.getClass().getClassLoader(), 
    new Class[]{Eat.class, Toilet.class},ac);//分别对应实际类的加载器,实际类的对应接口的类参数,调用处理器,参考反射,注意newProxyInstance生成的是接口类,可以为Object以及各种接口类如Eat,Toilet等,但是不能为Human类,因为非继承关系
    Eat proxyEat = (Eat) proxy;//将proxy代理转为Eat接口类进行Eat接口内方法的调用
    proxyEat.eat();
    Toilet proxyToil = (Toilet) proxy;
    proxyToil.toilet();
}

再考虑如何做到不对源代码修改呢?这就要考虑工厂方法了,就是利用函数返回去产生类。具体如下:

public class ActionHandler implements InvocationHandler{
    private Object instanceT;
    public ActionHandler(Object T){
        instanceT = T;
    }
    public Object invoke(Object proxy , Method method , Object[] args)
throws Throwable{
        wash();  //DIY方法
        return method.invoke(instanceT, args);//真正调用的方法,instanceT是之前传进来的具体实例,args是接口所需要的方法参数,详见反射
    }
    public Object bind(){
        return Proxy.newProxyInstance(instanceT.getClass().getClassLoader(), instanceT.getClass().getInterfaces(), this);//将proxy类的建立写在了调用处理器里面,注意这时候是对该类所有的接口都进行处理
    }
    static void wash(){
        System.out.println("washing");  
    }
}


//------------测试方法----------
public static void main(String[] args){
    Human h = new Human();
    ActionHandler action = new ActionHandler(h);
    Object proxy = action.bind();//这样主函数就不用import反射类了,并且这个ActionHandler类可以针对更广泛的类进行使用,比如Animal类等
    Eat proxyEat = (Eat) proxy;//将proxy代理转为Eat接口类进行Eat接口内方法的调用
    proxyEat.eat();
    Toilet proxyToil = (Toilet) proxy;
    proxyToil.toilet();

}

工厂方法的使用我理解的也不是很到位,希望有大大可以和我说下,谢谢咯!
参考知乎Java 动态代理作用是什么?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值