黑马程序员——Java代理模式与实例

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、Java代理模式简介
      即Proxy Pattern,23种java常用设计模式之一。代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。 
   代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在 客户端和目标对象之间起到中介的作用。 
      代理模式一般涉及到的角色有:      抽象角色:声明真实对象和代理对象的共同接口;      代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。      真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。  
二、什么时候使用代理模式?
      代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。
      在对已有的方法进行使用的时候出现需要对原有方法进行改进或者修改,这时候有2种改进选择:(1)修改原有方法来适应现在的使用方式;(2)或者用一个“第三者”方法来调用原有的方法并且对方法产生的结果进行一定的控制。第一种方法是明显违背了“程序可扩展、可维护的”最佳实践原则,而且在原来方法中作修改可能使得原来类的功能变得模糊和多元化;而使用第二种方法可以将功能划分得更加清晰,有助于后面的维护。所以在这种情况应当使用代理模式。
      下面列举一些应用场景会便于进一步理解代理模式。
三、代理模式应用场景
1.在原有功能上添加一些其他的功能。
假设有一个italk接口,有空的方法talk()(说话),所有的people对象都实现(implements)这个接口,实现talk()方法,前端有很多地方都将people实例化,执行talk方法,后来发现这些前端里有一些除了要说话以外还要唱歌(sing),根据程序最佳实践原则,那么我们既不能在Italk接口里增加sing()方法,又不能在每个前端都增加sing方法,我们只有增加一个代理类talkProxy,这个代理类里实现talk和sing方法,然后在需要sing方法的客户端调用代理类即可,代码如下:
接口类Italk
<span style="font-size:14px;">public interface Italk {

   public void talk(String msg);

}</span>
实现类people
public class People implements Italk {

  public String username;

  public String age;

  public String getName() {

  return username;

  }

  public void setName(String name) {

  this.username= name;

  }

  public String getAge() {

  return age;

  }

  public void setAge(String age) {

  this.age = age;

  }

  public People(String name1, String age1) {

  this.username= name1;

  this.age = age1;

  }

  public void talk(String msg) {

  System.out.println(msg+"!你好,我是"+username+",我年龄是"+age);

  }

}
代理类talkProxy
public class TalkProxy implements Italk {

  Italk talker;

  public TalkProxy (Italk talker) {

  //super();

  this.talker=talker;

  }

  public void talk(String msg) {

  talker.talk(msg);

  }

  public void talk(String msg,String singname) {

  talker.talk(msg);

  sing(singname);

  }

  private void sing(String singname){

  System.out.println("唱歌:"+singname);

  }

}
应用端myProxyTest
public class MyProxyTest {

  /**代理模式

  * @param args

  */

  public static void main(String[] args) {

  //不需要执行额外方法的

  Italk people1=new People("湖海散人","18");

  people1.talk("No ProXY Test");

  System.out.println("-----------------------------");

  //需要执行额外方法的

  TalkProxy talker=new TalkProxy(people1);

  talker.talk("ProXY Test","七里香");

  }

}
2.一个对象不想或者不能直接引用另一个对象。
比如在玩“极品飞车”这款游戏,如果游戏者手中的金钱达到了一定的数量就可以到车店买一部性能更高的赛车,那么这个卖车的“车店”就是一个典型的“汽车厂家”的“代理”,他为汽车厂家“提供卖车的服务”给有需求的人士。从面向对象的方面考虑,“销售汽车的代理”也是一个对象,那么这个对象也具有一定的状态,在软件项目中这个对象也具有管理财务进销存的基本功能,那么在设计时就要以面向OOP编程的思想来考虑软件的类结构,这个销售汽车的代理也是一个类了。
这里设People类是买车的目标对象,必须经过Proxy代理类的认证,符合条件的people才能进行买车。
新建买车接口:
public interface BuyCar{

    public void buy_car();

}
新建一个People人类,具有买车的行为,所以实现接口BuyCar:
public class People implements BuyCar {

    private int cash;
    private String username;

    public int getCash() {
        return cash;
    }

    public void setCash(int cash) {
        this.cash = cash;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
    public void buy_car() {
        System.out.println(username + "买了一台新车");
    }
}
新建代理类:
public class ProxyBuyCar implements BuyCar{

    private people people;

    public people getPeople() {
        return people;
    }

    public void setPeople(people people) {
        this.people = people;
    }

    public void buy_car() {
        if (people.getCash() > 3000) {
            System.out.println(people.getUsername() + "花" + people.getCash()
                    + "块 买了新车 交易结束");
        } else {
            System.out.println(people.getUsername() + "金钱不够,请继续比赛!");
        }
    }

}
创建客户端,模拟买车行为:
public class Test {
	
    public static void main(String[] args) {
        People people1 = new People();
        people1.setCash(4000);
        people1.setUsername("高洪岩");

        People people2 = new People();
        people2.setCash(2000);
        people2.setUsername("岩洪高");

        ProxyBuyCar proxy = new ProxyBuyCar();
        proxy.setPeople(people1);
        proxy.buy_car();

        proxy.setPeople(people2);
        proxy.buy_car();
    }
    
}
四、代理模式的另一种实现方式——Java动态代理模式
      在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生产JDK动态代理类或动态代理对象。
      Proxy提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态的创建实例,也可以使用Proxy来创建动态代理实例。
      Proxy提供了如下两个方法来创建动态代理类和动态代理实例:
      ——>static Class<?> getProxyClass(ClassLoader loader,Class<?>...interfaces):创建一个动态代理类所对应的Class对象。
      ——>static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h):直接创建一个动态代理对象。
      程序中可以采用先生存一个动态代理类,然后再通过动态代理类来创建代理对象的方式生成一个动态代理对象。代码片段如下:
//创建一个InvocationHandler对象

InvocationHandler handler = new MyInvocationHandler(...);

//使用Proxy生成一个动态代理类proxyClass

Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(),new Class[]{Foo.class});

//获取proxyClass类中带一个InvocationHandler参数的构造器

Constructor ctor = proxyClass.getConstructor(new Class[]{InvocationHandler.class});

//调用ctor的newInstance方法来创建动态实例

Foo f = (Foo)ctor.newInstance(new Object[]{handler});
上面代码也可以简化成如下代码:
//创建一个InvocationHandler对象

InvocationHandler handler = new MyInvocationHandler(...);

//使用Proxy直接生成一个动态代理对象

Foo f = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[]{Foo.class}, handler);
下面程序示范了使用Proxy和InvocationHandler来生成动态代理对象:
public class ProxyTest {
	
	public static void main(String[] args) throws Exception{
		InvocationHandler handler = new MyInvokationHandler();
		Person p = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, handler);
		p.walk();
		p.sayHello("孙悟空");
	}

}

interface Person{
	void walk();
	void sayHello(String name);
}

class MyInvokationHandler1 implements InvocationHandler{

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("-----正在执行的方法:"+method);
		if(args != null){
			System.out.println("下面是执行该方法时传入的实参为:");
			for(Object val : args){
				System.out.println(val);
			}
		}else{
			System.out.println("调用该方法没有实参!");
		}
		return null;
	}
	
}
动态代理的应用场景:

在实际开发中,总会存在相同的代码段重复出现的情况(如下图1),如果我们不断的进行复制、粘贴,在开发期间可能觉得无所谓,但如果有一天我们需要修改程序灰色代码的实现,则要打开3份源代码进行修改。如果有100个地方甚至1000个地方使用了这段灰色代码,那么修改、维护这段代码的工作量将变成噩梦。
在这种情况下,大部分稍有经验的开发者都会将这段深色代码定义成一个方法,然后让另外三段代码段直接调用该方法即可(如下图2)。
这时就需要使用代理模式,通过代理类将这些代码灵活的组合在一起。但即使这样,还会产生一个问题,代码段1、代码段2、代码段3和特定的方法耦合了!最理想的效果是:代码段1、2、3既可以执行深色代码部分,又无需在程序中以硬编码方法直接调用深色代码的方法。这时就需要用到代理模式的另一种实现,Java的动态代理模式!

如下程序:
Dog类:
public interface Dog {
	
	void info();
	void run();
}
Dog的实现类GunDog:
public class GunDog implements Dog{

	@Override
	public void info() {
		System.out.println("我是一只猎狗");
	}

	@Override
	public void run() {
		System.out.println("我奔跑迅速");
	}

}
封装通用代码段的DogUtil类:
public class DogUtil {
	
	public void method1(){
		System.out.println("=====模拟第一个通用方法======");
	}
	
	public void method2(){
		System.out.println("=====模特第二个通用方法======");
	}

}
MyInvocationHandler类:
public class MyInvokationHandler implements InvocationHandler{
	
	private Object target;
	public void setTarget(Object target){
		this.target = target;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		DogUtil du = new DogUtil();
		du.method1();
		Object result = method.invoke(target, args);
		du.method2();
		return null;
	}

}
MyProxyFactory类,该对象专为指定的target生成动态代理实例:
public class MyProxyFactory {
	
	public static Object getProxy(Object target) throws Exception{
		MyInvokationHandler handler = new MyInvokationHandler();
		handler.setTarget(target);
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
	}

}
测试类:
public class Test {
	
	public static void main(String[] args) throws Exception{
		Dog target = new GunDog();
		Dog dog = (Dog)MyProxyFactory.getProxy(target);
		dog.info();
		dog.run();
	}

}
输出:
=====模拟第一个通用方法======
我是一只猎狗
=====模特第二个通用方法======
=====模拟第一个通用方法======
我奔跑迅速
=====模特第二个通用方法======


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值