代理模式
什么是代理模式?
代理模式就像请一个"替身"来帮你做事。想象一下,你想买房子,但你没有时间去看房,于是就请了一个房产中介(代理)来帮你找房子、看房子、谈价格。这个中介就是你的"代理",他代表你去做这些事情。
在编程中,代理模式就是创建一个代理对象来控制对另一个对象的访问。代理对象可以在访问真实对象之前或之后添加一些额外的处理逻辑。
代理模式的三种类型
1. 静态代理
静态代理是在编译时就确定代理关系的代理模式。
2. 动态代理
动态代理是在运行时动态创建代理对象的代理模式。
3. 虚拟代理
虚拟代理用于延迟加载,只有在真正需要时才创建真实对象。
示例一:房产中介代理(静态代理)
// 1. 定义接口
interface HouseService {
void findHouse();
void negotiatePrice();
void signContract();
}
// 2. 真实对象(房主)
class HouseOwner implements HouseService {
@Override
public void findHouse() {
System.out.println("房主:我的房子在市中心,3室2厅,120平米");
}
@Override
public void negotiatePrice() {
System.out.println("房主:最低价格200万,不能再低了");
}
@Override
public void signContract() {
System.out.println("房主:好的,我们签合同吧");
}
}
// 3. 代理对象(房产中介)
class HouseAgent implements HouseService {
private HouseOwner houseOwner;
public HouseAgent() {
this.houseOwner = new HouseOwner();
}
@Override
public void findHouse() {
System.out.println("中介:我来帮您找房子");
houseOwner.findHouse();
System.out.println("中介:这个房子位置很好,价格合理");
}
@Override
public void negotiatePrice() {
System.out.println("中介:我来帮您谈价格");
houseOwner.negotiatePrice();
System.out.println("中介:经过我的努力,价格可以再优惠5万");
}
@Override
public void signContract() {
System.out.println("中介:我来帮您办理签约手续");
houseOwner.signContract();
System.out.println("中介:合同已签,收取中介费2%");
}
}
// 4. 客户端使用
public class StaticProxyDemo {
public static void main(String[] args) {
HouseService agent = new HouseAgent();
agent.findHouse();
agent.negotiatePrice();
agent.signContract();
}
}
示例二:图片加载代理(虚拟代理)
// 1. 图片接口
interface Image {
void display();
}
// 2. 真实图片类(重量级对象)
class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("正在加载图片: " + fileName);
// 模拟加载时间
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("图片加载完成: " + fileName);
}
@Override
public void display() {
System.out.println("显示图片: " + fileName);
}
}
// 3. 图片代理类
class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}
// 4. 客户端使用
public class VirtualProxyDemo {
public static void main(String[] args) {
Image image1 = new ProxyImage("photo1.jpg");
Image image2 = new ProxyImage("photo2.jpg");
System.out.println("第一次显示图片1:");
image1.display();
System.out.println("\n第一次显示图片2:");
image2.display();
System.out.println("\n第二次显示图片1(已缓存):");
image1.display();
}
}
示例三:权限控制代理(动态代理)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 1. 用户服务接口
interface UserService {
void viewProfile();
void editProfile();
void deleteAccount();
}
// 2. 用户服务实现
class UserServiceImpl implements UserService {
@Override
public void viewProfile() {
System.out.println("查看用户资料");
}
@Override
public void editProfile() {
System.out.println("编辑用户资料");
}
@Override
public void deleteAccount() {
System.out.println("删除用户账户");
}
}
// 3. 权限检查处理器
class PermissionHandler implements InvocationHandler {
private Object target;
private String userRole;
public PermissionHandler(Object target, String userRole) {
this.target = target;
this.userRole = userRole;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
// 权限检查
if (methodName.equals("deleteAccount") && !"admin".equals(userRole)) {
System.out.println("权限不足:只有管理员可以删除账户");
return null;
}
if (methodName.equals("editProfile") && "guest".equals(userRole)) {
System.out.println("权限不足:游客不能编辑资料");
return null;
}
// 记录日志
System.out.println("用户角色: " + userRole + ", 执行方法: " + methodName);
// 调用真实方法
return method.invoke(target, args);
}
}
// 4. 客户端使用
public class DynamicProxyDemo {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
// 创建普通用户代理
UserService normalUserProxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class<?>[] { UserService.class },
new PermissionHandler(userService, "user")
);
// 创建游客代理
UserService guestProxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class<?>[] { UserService.class },
new PermissionHandler(userService, "guest")
);
// 创建管理员代理
UserService adminProxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class<?>[] { UserService.class },
new PermissionHandler(userService, "admin")
);
System.out.println("=== 普通用户操作 ===");
normalUserProxy.viewProfile();
normalUserProxy.editProfile();
normalUserProxy.deleteAccount();
System.out.println("\n=== 游客操作 ===");
guestProxy.viewProfile();
guestProxy.editProfile();
guestProxy.deleteAccount();
System.out.println("\n=== 管理员操作 ===");
adminProxy.viewProfile();
adminProxy.editProfile();
adminProxy.deleteAccount();
}
}
代理模式的优点
- 控制访问:可以在访问真实对象前后添加额外的控制逻辑
- 延迟加载:虚拟代理可以延迟创建重量级对象
- 权限控制:可以控制对真实对象的访问权限
- 日志记录:可以记录对真实对象的访问日志
- 缓存:可以缓存真实对象的结果
代理模式的缺点
- 增加复杂性:引入了额外的代理层,增加了系统的复杂性
- 性能开销:代理对象会带来一定的性能开销
- 代码冗余:静态代理需要为每个代理类编写大量重复代码
使用场景
- 远程代理:控制对远程对象的访问
- 虚拟代理:延迟加载重量级对象
- 保护代理:控制对敏感对象的访问权限
- 缓存代理:为开销大的操作提供缓存
- 日志代理:记录对对象的访问日志
总结
代理模式就像生活中的各种"中介"一样,它们代表真实对象处理各种事务,并在处理过程中添加自己的逻辑。这种模式让我们可以在不修改原有代码的情况下,为对象添加新的功能和行为。
