• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【设计模式与体系结构】结构型模式-代理模式

简介

代理模式(Proxy Pattern)是一种结构型设计模式。它为其他对象提供一种代理,以控制对这个对象的访问。简单来说,当客户端不方便直接访问一个对象,或者需要在访问这个对象之前或之后执行一些额外的操作时,就可以使用代理对象来代替目标对象进行操作。

代理模式的角色

  • 抽象主题角色Subject:声明具体主题和代理主题的共同接口,这样一来任何使用具体主题的地方都可以使用代理主题,客户端需要遵循依赖倒转原则,即面向抽象编程
  • 具体主题角色ConcreteSubject:实现抽象主题中声明的接口方法
  • 代理主题角色Proxy:包含对具体主题的引用,从而可以在任何时候操作具体主题对象,在代理主题角色中提供一个与具体主题角色相同的接口,以便在任何时候都可以替代具体主题;代理主题角色还可以控制对具体主题的使用,负责在需要的时候创建和删除具体主题对象,并对具体主题对象的使用加以约束。

代理模式的类型

  1. 远程代理(Remote Proxy):当需要访问位于远程服务器上的对象时使用,它使得客户端可以像访问本地对象一样访问远程对象。
  2. 虚拟代理(Virtual Proxy):当创建一个对象的成本很高,或者对象的创建很耗时,但又不是立即需要这个对象的全部功能时使用。
  3. 保护代理(Protection Proxy):当需要对目标对象进行访问控制,根据不同的用户权限提供不同程度的访问时使用。
  4. 智能代理(Smart Proxy):当需要在访问目标对象的过程中添加一些额外的智能行为,如引用计数、自动垃圾回收等情况时使用。
  5. 缓冲代理(Buffer Proxy):当对象会被频繁访问相同资源时,可以使用缓冲代理,对数据进行缓冲,从而提高数据的访问效率。

记忆口诀:想要办代理,只需元宝换(智虚远保缓)。

代理模式的优点

  1. 增强安全性:通过保护代理,可以对目标对象进行权限控制,防止未经授权的访问,保护系统的安全性和数据的完整性。
  2. 提高性能:利用虚拟代理可以延迟对象的创建,避免不必要的资源消耗。例如,在网页加载图片的场景中,避免了一次性加载大量高分辨率图片导致的性能问题。
  3. 降低复杂度:远程代理可以隐藏远程对象访问的网络通信等复杂细节,使得客户端的代码更加简洁,易于维护和理解。

代理模式的缺点

  1. 增加系统复杂度:引入代理对象会增加代码的复杂性。因为需要同时维护代理对象和真实对象,以及它们之间的关系。如果代理的逻辑比较复杂,可能会导致代码难以理解和调试。
  2. 降低性能(部分情况):如果代理对象的额外操作(如权限验证等)过于复杂或者频繁,可能会对系统性能产生一定的影响。不过,在合理使用的情况下,这种影响通常是可以接受的,并且可以通过优化代理的操作来减轻这种影响。

代理模式的使用场景

  1. 当客户端对象需要访问远程主机中的对象时可以使用远程代理。
  2. 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
  3. 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
  4. 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
  5. 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。

正文

虚拟代理(Virtual Proxy)

虚拟代理(Virtual Proxy)可以延迟一个资源消耗大的对象的创建,从而达到提高性能的作用。例如一家餐厅里面顾客一般是先由服务员接待,而不是直接由厨师来接待,只有当顾客向服务员点餐完成,服务员才将做菜的任务交由厨师完成。

定义一个餐厅工作人员接口 ICook.java

public interface ICook {
	void cook();//做菜
	void add(String delicacies);//点菜
}

定义一个厨师类 Cook.java

public class Cook implements ICook {
	private List<String> list = new LinkedList<String>();
	
	@Override
	public void cook() {
		for (String s: list) {
			System.out.println("做了一道菜:" + s);
		}
		list.removeAll(list);
	}

	@Override
	public void add(String delicacies) {
		System.out.println("厨师在菜单上加了一道菜:" + delicacies);
		list.add(delicacies);
	}

}

定义一个服务员类 Waiter.java

public class Waiter implements ICook {
	private Cook cook;
	private List<String> list = new LinkedList<String>();
	
	@Override
	public void cook() {
		System.out.println("服务员把做菜的任务转交给了厨师");
		if (cook == null) cook = new Cook();
		for (String s: list) {
			cook.add(s);
		}
		list.removeAll(list);
		cook.cook();
	}


	@Override
	public void add(String delicacies) {
		if (cook != null) {
			cook.add(delicacies);
		} else {
			System.out.println("服务员在菜单上加了一道菜:" + delicacies);
			list.add(delicacies);
		}
	}

}

最后写一个消费者类 Consumer.java,调用服务员类的虚拟代理。

public class Consumer {
	public static void main(String[] args) {
		//定义一个服务员对象
		ICook waiter = new Waiter();
		
		//顾客向服务员点菜,服务员先自己记录
		waiter.add("红烧黄河大鲤鱼");
		waiter.add("鲶鱼炖茄子");
		waiter.add("佛跳墙");
		waiter.add("北京烤鸭");
		waiter.add("荔浦芋头炒肉");
		
		//服务员记录完菜单,自然是要进入到做菜环境,但是服务员不负责做菜,而是要转交给厨师负责
		waiter.cook();
		
		//顾客加菜,由于厨师对象已经实例化了,因此加菜的任务直接交给厨师即可
		waiter.add("蔬菜沙拉");
		waiter.add("水果拼盘");
		
		//厨师把加的菜做了
		waiter.cook();
	}
}

运行效果截图如下:

保护代理(Protection Proxy)

保护代理(Protection Proxy)可以对不同用户提供不同程度的访问。例如公司里的普通员工没有发布公告的权限,但是老板具有发布公告的权限。

定义一个员工接口 Employee.java

public interface Employee {
	void publish(String text);
}

定义一个员工实体类 EmployeeImpl.java

public class EmployeeImpl implements Employee {
    
    //定义一个发布公告的方法,明显普通员工没有发布公告的权限,高层员工有发布公告的权限
	@Override
	public void publish(String text) {
		System.out.println("发布公告:" + text);
	}
	
}

Proxy类:Java反射包自带,其中newProxyInstance可以返回接口实现类的实例。因此可以使用Proxy类来反射到员工实体类的方法调用,以此做出功能的保护。

定义一个普通员工的引用类 EmployeeInvocationHandler.java

public class EmployeeInvocationHandler implements InvocationHandler {
	private Employee employee;
	
	public EmployeeInvocationHandler(Employee employee) {
		this.employee = employee;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if ("publish".equals(method.getName())) {
			throw new IllegalAccessException("普通员工不能发布公告");
		}
		return method.invoke(employee, args);
	}

}

定义一个高层员工的引用类 EmployerInvocationHandler.java

public class EmployerInvocationHandler implements InvocationHandler {
	private Employee employee;
	
	public EmployerInvocationHandler(Employee employee) {
		this.employee = employee;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if ("publish".equals(method.getName())) {
			return method.invoke(employee, args);
		}
		return null;
	}

}

定义一个客户端 Client.java

public class Client {
	public static void main(String[] args) {
		Employee employee1 = new EmployeeImpl();
		Employee employee2 = new EmployeeImpl();
		Employee employeeProxy2 = getEmployerProxy(employee2);
		employeeProxy2.publish("bbbb");
		Employee employeeProxy1 = getEmployeeProxy(employee1);
		employeeProxy1.publish("aaaa");
		
	}
	
	private static Employee getEmployeeProxy(Employee employee) {
		return (Employee) Proxy.newProxyInstance(employee.getClass().getClassLoader(), 
				employee.getClass().getInterfaces(), 
				new EmployeeInvocationHandler(employee));
	}
	
	private static Employee getEmployerProxy(Employee employee) {
		return (Employee) Proxy.newProxyInstance(employee.getClass().getClassLoader(), 
				employee.getClass().getInterfaces(), 
				new EmployerInvocationHandler(employee));
	}
}

运行效果截图如下:

总结

与中介者模式、外观模式的区别

答:代理模式是代理一个子系统的部分功能;外观模式是集中子系统的功能组合,对外提供一致的使用接口;中介者模式是针对子系统内部模块之间,通过一个中介者简化各对象间的相互引用。

posted on 2025-01-17 23:56  RomanLin  阅读(57)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3