• 3. 狂神的设计模式笔记-代理模式


    本文笔记来自于:狂神的设计模式

    代理模式的分类:

    • 静态代理
    • 动态代理

    一、静态代理

    1.1 角色分析:

    • 抽象角色:一般会使用接口或者抽象类来解决
    • 真实角色:被代理的角色(eg:房东)
    • 代理角色:代理真实角色后,我们一般会做一些附属操作(eg:中介)
    • 客户:访问对象的人(eg:租房的人)


    1.2 代码实现

    1. 首先创建抽象角色,将共同业务放入其中

      public interface Rent {
      	void rent();//租房
      }
      
    2. 创建房东,继承租房接口

      //房东
      public class Host implements Rent{
      	@Override
      	public void rent() {
      		System.out.println("房东租房");
      	}
      }
      
      
    3. 创建中介,继承租房接口,并扩展业务

      //中介代理
      public class Proxy implements Rent{
      	
      	private Host host;
      	public Proxy(Host host) {
      		this.host = host;
      	}
      	@Override
      	public void rent() {
      		System.out.println("代理帮忙租房!");	
      		host.rent();
      	}
      	public void look() {
      		System.out.println("帮忙看房");
      	}
      	public void agreement() {
      		System.out.println("签署合同");
      	}
      }
      
    4. 创建客户

      //客户
      public class Client {
      	public static void main(String[] args) {
      		Host host = new Host();
      		Proxy proxy = new Proxy(host);
      		proxy.look();
      		proxy.rent();
      		proxy.agreement();
      	}
      }
      

      调用结果如下:

      帮忙看房
      代理帮忙租房!
      房东租房
      签署合同

    代理模式的好处:

    ● 可以使真实角色的操作更加纯粹!不用去关注一 些公共的业务
    ● 公共业务交给代理角色!实现了业务的分工!
    ● 公共业务发生扩展的时候,方便集中管理!

    缺点:
    ● 一个真实角色就会产生一个代理角色;代码量会翻倍开发效率会变低~


    1.3 深入理解静态代理

    在现实的项目中,如果我们之前的项目已经写好了接口(比如增删改查),那么我们需要增强功能的时候,不需要修改原来的代码(在公司中修改原有的代码是大忌),而是增加一个代理类,达到增强业务的目的。

    原有接口和实现方法:

    public interface UserService {
    	void add();
    	void delete();
    	void update();
    	void query();
    }
    
    public class UserServiceImpl implements UserService {
    
    	@Override
    	public void add() {
    		System.out.println("增加");//执行的操作
    	}
    
    	@Override
    	public void delete() {
    		System.out.println("删除");
    	}
    
    	@Override
    	public void update() {
    		System.out.println("更新");
    	}
    
    	@Override
    	public void query() {
    		System.out.println("查询");
    	}
    }
    

    这时候我们的业务需要在执行每个操作前,输出日志,我们不需要去修改UserServiceImpl

    的代码,而是去增加一个代理类UserProxy

    public class UserProxy implements UserService{
    	
    	private UserServiceImpl userServiceImpl;
    	
    	public void userProxy(UserServiceImpl userServiceImpl) {
    		this.userServiceImpl = userServiceImpl;
    	}
    
    	@Override
    	public void add() {
    		System.out.println("[DEBUG]: 调用了add方法");
    		userServiceImpl.add();
    	}
    
    	@Override
    	public void delete() {
    		System.out.println("[DEBUG]: 调用了delete方法");
    		userServiceImpl.delete();
    	}
    
    	@Override
    	public void update() {
    		System.out.println("[DEBUG]: 调用了update方法");
    		userServiceImpl.update();
    	}
    
    	@Override
    	public void query() {
    		System.out.println("[DEBUG]: 调用了query方法");
    		userServiceImpl.query();
    	}
    }
    

    二、动态代理

    • 动态代理和静态代理角色一样
    • 动态代理的代理类是动态生成的,不是我们直接写好的
    • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
      • 基于接口–JDK动态代理
      • 基于类:cglib

    需要了解两个类:Proxy 和 InvocationHandler

    2.1 代码实现

    还是以UserService的例子来说:

    public interface UserService {
    	void add();
    	void delete();
    	void update();
    	void query();
    }
    
    public class UserServiceImpl implements UserService {
    
    	@Override
    	public void add() {
    		System.out.println("增加");
    	}
    
    	@Override
    	public void delete() {
    		System.out.println("删除");
    	}
    
    	@Override
    	public void update() {
    		System.out.println("更新");
    	}
    
    	@Override
    	public void query() {
    		System.out.println("查询");
    	}
    }
    

    不过我们动态代理里面不需要自己写代理类了,而是使用一个生成代理类的机制,需要扩展的方法在invoke中调用:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    //代理调用处理程序
    //会用这个类自动生成代理类
    public class ProxyInvocatinHandler implements InvocationHandler{
    
    	//被代理的接口
    	private Object in;
    
    	public void setRent(Object in) {
    		this.in = in;
    	}
    
    	public Object getProxy() {
    		 return Proxy.newProxyInstance(this.getClass().getClassLoader(), in.getClass().getInterfaces(), this);
    	}
    	
    	//处理代理实例,并返回结果
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		//动态代理的本质,就是使用反射实现的
    		log(method.getName());//需要扩展的业务
    		Object result = method.invoke(in, args);
    		return result;
    	}
    	
    	private void log(String msg) {
    		System.out.println("[DEBUG]执行了 "+msg+" 的方法");
    	}
    }
    
    

    然后我们使用这个机制,根据被代理的对象自动生成代理类:

    public class Client {
    	public static void main(String[] args) {
    		//真实角色
    		Host host = new Host();
    		//代理角色
    		ProxyInvocatinHandler pih = new ProxyInvocatinHandler();
    		//通过调用程序处理角色
    		pih.setRent(host);
    		//获取代理
    		Rent proxy = (Rent)pih.getProxy();
    		proxy.rent();
    	}
    }
    

    运行结果:

    [DEBUG]执行了 add 的方法
    增加

    应用场景:下次需要在某个方法前打印日志时,可以直接使用这个动态代理的处理类来代理,不受接口类型的限制,同样可以运用到房东买房的例子

    动态代理的好处:
    ● 可以使真实角色的操作更加纯粹!不用去关注一 些公共的业务
    ● 公共也就就交给代理角色!实现了业务的分工!
    ● 公共业务发生扩展的时候,方便集中管理!
    ● 一个动态代理类代理的是一个接口,一般就是对应的一类业务

    	//获取代理
    		Rent proxy = (Rent)pih.getProxy();
    		proxy.rent();
    	}
    }
    

    运行结果:

    [DEBUG]执行了 add 的方法
    增加

    应用场景:下次需要在某个方法前打印日志时,可以直接使用这个动态代理的处理类来代理,不受接口类型的限制,同样可以运用到房东买房的例子

    动态代理的好处:
    ● 可以使真实角色的操作更加纯粹!不用去关注一 些公共的业务
    ● 公共也就就交给代理角色!实现了业务的分工!
    ● 公共业务发生扩展的时候,方便集中管理!
    ● 一个动态代理类代理的是一个接口,一般就是对应的一类业务

    ●一个动态代理类可以代理多个类,只要是实现了同一个接口即可

  • 相关阅读:
    KNN算法--物以类聚,人以群分
    朴素贝叶斯算法原理
    17.Letter Combinations of a Phone Number
    103.Binary Tree Zigzag Level Order Traversal
    65、使用互斥锁唤醒指定线程
    64、线程之间的通信
    63、使用Timer类来实现定时任务
    62、单例模式
    61.volatile关键字
    60、死锁
  • 原文地址:https://www.cnblogs.com/theory/p/13338734.html
Copyright © 2020-2023  润新知