# 代理模式

# 概念

代理模式是一种结构型设计模式,其核心思想是为其他对象提供一种代理,以控制对该对象的访问。代理对象作为客户端和目标对象之间的中介,能够在目标对象执行操作前后添加额外的功能。

# 作用

1.控制对象访问:通过代理对象作为中介,可以管理对目标对象的访问权限,例如根据用户权限决定是否允许访问。

2.增强功能:在不修改目标对象代码的情况下,为其方法添加前置或后置操作,如日志记录、性能监控等。

3.延迟加载:代理对象可以延迟目标对象的实例化,直到真正需要时才创建,避免过早占用内存资源。

4.职责分离:将核心业务逻辑与附加功能分离,符合单一职责原则,提升代码的可维护性。

# 场景

1.访问控制:需要根据用户权限决定是否允许访问目标对象时,如权限校验。

2.日志记录:在访问目标对象前后记录日志信息,便于审计或调试。

3.性能监控:测量目标对象方法的执行时间,用于性能分析或优化。

4.延迟加载:当目标对象创建成本较高时,按需加载资源,如数据库连接或大型对象。

# 举例

# 静态代理

// 目标接口
public interface ITeacher {
    void teach();
}
// 目标类
public class Teacher implements ITeacher {
    @Override
    public void teach() {
        System.out.println("老师正在授课");
    }
}
// 代理类
public class TeacherProxy implements ITeacher {
    private Teacher teacher;

    public TeacherProxy(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    public void teach() {
        // 前置操作
        System.out.println("开始上课前的准备工作");
        teacher.teach();
        // 后置操作
        System.out.println("课程结束,安排课后作业");
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        TeacherProxy proxy = new TeacherProxy(teacher);
        proxy.teach();
    }
}

# 动态代理

// 目标接口
public interface ITeacher {
    void teach();
}
// 目标类
public class Teacher implements ITeacher {
    @Override
    public void teach() {
        System.out.println("老师正在授课");
    }
}
// 动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxy implements InvocationHandler {
    private Object target;

    public DynamicProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置操作
        System.out.println("开始上课前的准备工作");
        Object result = method.invoke(target, args);
        // 后置操作
        System.out.println("课程结束,安排课后作业");
        return result;
    }

    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        DynamicProxy proxy = new DynamicProxy(teacher);
        ITeacher iTeacher = (ITeacher) proxy.getProxyInstance();
        iTeacher.teach();
    }
}

# 反例

如果不使用代理模式,可能需要通过继承目标类并重写方法来实现附加功能,例如:

public class EnhancedTeacher extends Teacher {
    @Override
    public void teach() {
        // 前置操作
        System.out.println("开始上课前的准备工作");
        super.teach();
        // 后置操作
        System.out.println("课程结束,安排课后作业");
    }
}

这种方式存在以下问题:

1.侵入性修改:需要修改目标类的代码或通过继承来增强功能,违反了开闭原则(对扩展开放,对修改关闭)。

2.复用性差:如果多个目标类需要相同的附加功能(如日志记录),需要为每个类创建子类,导致代码重复。

3.耦合度高:客户端直接依赖具体的增强类(如EnhancedTeacher),当目标类或附加功能变化时,需要修改客户端代码,不利于系统的扩展和维护。

# 原理

代理模式通过引入代理对象,拦截客户端对目标对象的访问请求。在静态代理中,代理类与目标类的关系在编译时确定,通过组合目标对象并在其方法调用前后插入额外逻辑。动态代理则利用反射机制,在运行时动态生成代理类,能够为任何实现了接口的目标对象创建代理,具有更高的灵活性和复用性。

# 缺点

1.性能开销:动态代理在运行时通过反射调用方法,可能带来一定的性能损耗;静态代理虽然性能较好,但需要为每个目标类编写代理类,增加代码量。

2.代码复杂度:无论是静态代理还是动态代理,都需要额外编写代理类或处理代理逻辑的代码,增加了系统的复杂度和维护成本。

总结

代理模式通过代理对象控制对目标对象的访问,并在访问过程中添加额外功能,实现了核心逻辑与附加功能的解耦。其静态代理形式在编译时确定代理关系,动态代理则在运行时生成代理类,提供了更高的灵活性。该模式适用于访问控制、日志记录、性能监控等场景,能够有效避免直接修改目标类代码带来的侵入性问题。然而,代理模式可能引入性能开销和代码复杂度,需根据具体场景权衡使用。



微信公众号

QQ交流群
原创网站开发,偏差难以避免。

如若发现错误,诚心感谢反馈。

愿你倾心相念,愿你学有所成。

愿你朝华相顾,愿你前程似锦。