代理模式和装饰器模式有什么不同?

968 阅读5分钟

1. 代理模式

代理模式(Proxy Pattern)为其他对象提供一种代理,以控制对这个对象的访问。在Java中,代理模式主要有两种实现方式:静态代理和动态代理。下面我们分步骤简单介绍静态代理和动态代理的实现。

静态代理

  1. 定义一个接口,声明代理对象和被代理对象需要实现的方法。
public interface Subject {
    void request();
}
  1. 创建被代理对象,实现Subject接口。
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("Request in RealSubject");
    }
}
  1. 创建代理对象,实现Subject接口。在代理对象中持有被代理对象的引用,并重写接口中的方法,在方法调用前后添加自定义操作。
public class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        System.out.println("Request in ProxySubject (before)");
        realSubject.request();
        System.out.println("Request in ProxySubject (after)");
    }
}
  1. 在客户端代码中,使用代理对象来代替被代理对象进行操作。
public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
        proxySubject.request();
    }
}

运行客户端代码,输出结果如下:

Request in ProxySubject (before)
Request in RealSubject
Request in ProxySubject (after)

以上便是Java中静态代理模式的简单实现。通过代理对象,我们可以在被代理对象的方法调用前后添加自定义操作,实现对被代理对象的访问控制。

动态代理

Java的动态代理主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现。动态代理相比静态代理,更加灵活,不需要为每一个被代理对象都创建一个代理类。

下面是一个简单的Java动态代理实现步骤:

  1. 首先定义一个接口,声明代理对象和被代理对象需要实现的方法。
public interface Subject {
    void request();
}
  1. 创建被代理对象,实现Subject接口。
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("Request in RealSubject");
    }
}
  1. 创建InvocationHandler接口的实现类,重写invoke方法,在方法调用前后添加自定义操作。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    public DynamicProxyHandler(Object proxied) {
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Request in DynamicProxyHandler (before)");
        Object result = method.invoke(proxied, args);
        System.out.println("Request in DynamicProxyHandler (after)");
        return result;
    }
}
  1. 在客户端代码中,使用Proxy.newProxyInstance()方法创建动态代理对象,传入被代理对象的类加载器、实现的接口数组和InvocationHandler实例。
import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
            RealSubject.class.getClassLoader(),
            new Class[]{Subject.class},
            new DynamicProxyHandler(realSubject));
        proxySubject.request();
    }
}

运行客户端代码,输出结果如下:

Request in DynamicProxyHandler (before)
Request in RealSubject
Request in DynamicProxyHandler (after)

以上便是Java中动态代理模式的简单实现。通过动态代理,我们可以在运行时动态创建代理对象,为不同的被代理对象添加相同的访问控制逻辑,避免了为每一个被代理对象都创建一个代理类的繁琐。

装饰器模式.png 2. 装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它在一个已有对象上动态的添加更多的功能。

装饰器模式的基本实现步骤如下:

  1. 首先定义一个接口,声明装饰对象和被装饰对象需要实现的方法。
public interface Component {
    void operation();
}
  1. 创建被装饰对象,实现Component接口。
public class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("Operation in ConcreteComponent");
    }
}
  1. 创建抽象装饰器类,持有Component接口的引用,并实现Component接口。
public abstract class Decorator implements Component {
    protected Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        component.operation();
    }
}
  1. 创建具体装饰器类,继承抽象装饰器类,并在方法调用前后添加自定义操作。
public class ConcreteDecorator extends Decorator {
    public ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        System.out.println("Operation in ConcreteDecorator (before)");
        super.operation();
        System.out.println("Operation in ConcreteDecorator (after)");
    }
}
  1. 在客户端代码中,使用具体装饰器类来装饰被装饰对象。
public class Client {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Component decoratedComponent = new ConcreteDecorator(component);
        decoratedComponent.operation();
    }
}

运行客户端代码,输出结果如下:

Operation in ConcreteDecorator (before)
Operation in ConcreteComponent
Operation in ConcreteDecorator (after)

以上就是Java中装饰器模式的简单实现。通过装饰器模式,我们可以在不修改被装饰对象的基础上,动态地给被装饰对象添加新的功能。

3. 代理模式和装饰器模式有什么不同?

代理模式和装饰器模式在表面上看起来相似,都涉及到了一个类代表另一个类的功能,但是他们的用途和设计目标是不同的。以下是他们之间的主要区别:

  1. 意图与目的

    • 代理模式的目的主要是控制对其他对象的访问,通常是因为你不能或不想直接访问该对象。
    • 装饰器模式的目的是增强或修改对象的功能。装饰器在执行方法时会执行其自己的行为,然后调用原始对象的方法,可能在此之前,也可能在此之后,甚至可能完全不调用。
  2. 使用场景

    • 代理模式常常用于网络操作,或者其他需要安全控制、复杂性管理的操作。例如,创建一个对象可能需要大量的计算资源和时间,代理模式可以用于懒加载这个对象,只有当真正需要它的时候才创建。
    • 装饰器模式用于动态地添加行为和责任到对象上。例如,Java中的IO类使用了装饰器模式,可以动态地给一个流添加不同的功能,如缓冲、字符编码、压缩等。
  3. 设计

    • 在代理模式中,代理类控制对原对象的引用,客户端通常不知道原对象,只知道代理对象。
    • 在装饰器模式中,客户端通常需要处理装饰器类和原始对象,必须有一种方法可以从装饰器访问原始对象。
  4. 数量

    • 在代理模式中,通常只会有一个代理与原始对象进行交互。
    • 在装饰器模式中,我们可以使用一个或多个装饰器来装饰一个对象。

这些差异使得代理模式和装饰器模式在实际使用中有不同的适用场景。