MST

星途 面试题库

面试题:Java装饰器模式与代理模式的区别

在Java中,装饰器模式和代理模式在结构上有一定相似性。请详细分析装饰器模式与代理模式的区别,包括意图、使用场景以及实现方式上的不同点,并结合具体代码示例说明。
27.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

意图区别

  • 装饰器模式:主要意图是在不改变原有对象结构的情况下,动态地给对象添加新的功能。它强调的是对功能的增强,就像给一个对象“穿衣服”,一件一件地添加功能。
  • 代理模式:意图是为其他对象提供一种代理以控制对这个对象的访问。它主要用于控制对目标对象的访问,比如权限控制、延迟加载等,就像是一个“中介”,通过它来访问目标对象。

使用场景区别

  • 装饰器模式:当需要在运行时给对象添加新功能,且不希望通过继承来实现(避免子类过多)时使用。例如,在Java的I/O流中,BufferedInputStream就是通过装饰器模式给InputStream添加了缓冲功能。
  • 代理模式:适用于需要对目标对象的访问进行控制,如远程代理(访问远程对象)、虚拟代理(延迟加载大对象)、保护代理(控制访问权限)等场景。比如,在访问数据库连接池时,使用代理来控制对连接的获取和释放。

实现方式区别

  • 装饰器模式:装饰器和被装饰对象实现同一个接口,装饰器内部持有被装饰对象的引用。在装饰器的方法中,调用被装饰对象的方法,并在前后添加额外功能。
  • 代理模式:代理类和目标类实现同一个接口,代理类内部持有目标类的引用。代理类在方法中控制对目标类方法的调用,如进行权限检查、记录日志等操作。

代码示例

装饰器模式示例

// 抽象组件
interface Shape {
    void draw();
}

// 具体组件
class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Rectangle");
    }
}

// 抽象装饰器
abstract class ShapeDecorator implements Shape {
    protected Shape decoratedShape;

    public ShapeDecorator(Shape decoratedShape) {
        this.decoratedShape = decoratedShape;
    }

    @Override
    public void draw() {
        decoratedShape.draw();
    }
}

// 具体装饰器
class RedShapeDecorator extends ShapeDecorator {
    public RedShapeDecorator(Shape decoratedShape) {
        super(decoratedShape);
    }

    @Override
    public void draw() {
        decoratedShape.draw();
        setRedBorder(decoratedShape);
    }

    private void setRedBorder(Shape decoratedShape) {
        System.out.println("Border Color: Red");
    }
}

// 测试
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        Shape rectangle = new Rectangle();
        Shape redRectangle = new RedShapeDecorator(new Rectangle());

        rectangle.draw();
        redRectangle.draw();
    }
}

代理模式示例

// 抽象接口
interface Image {
    void display();
}

// 真实主题
class RealImage implements Image {
    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk(fileName);
    }

    @Override
    public void display() {
        System.out.println("Displaying " + fileName);
    }

    private void loadFromDisk(String fileName) {
        System.out.println("Loading " + fileName);
    }
}

// 代理主题
class ProxyImage implements Image {
    private String fileName;
    private RealImage realImage;

    public ProxyImage(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }
        realImage.display();
    }
}

// 测试
public class ProxyPatternDemo {
    public static void main(String[] args) {
        Image image = new ProxyImage("test_10mb.jpg");

        // 图像将从磁盘加载
        image.display();
        System.out.println("");
        // 图像不需要从磁盘加载
        image.display();
    }
}