装饰器模式(Decorator Pattern)和代理模式(Proxy Pattern)都是结构型设计模式,它们在结构上看起来相似,因为两者都是通过包装一个类来扩展其功能。但它们的意图、用途和实现方式存在明显差异。

装饰器模式:

目的:

  • 主要用于添加新的功能到对象,而不改变其接口。
  • 通过添加“装饰”的方式动态地给对象附加额外的职责。
  • 可以灵活地组合多个装饰器来增强对象的行为。

适用场景:

  • 当需要透明且动态地扩展类的功能时。
  • 当希望一个对象有多种变化,或者对象的变化可以组合时。
  • 在不想增加很多子类的情况下扩展类。

Java示例:

假设我们有一个简单的TextView类,我们想动态地为其添加边框和滚动条而不修改原始类。

// 基本组件接口
interface VisualComponent {
    void draw();
}

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

// 抽象装饰器
abstract class Decorator implements VisualComponent {
    protected VisualComponent component;

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

    public void draw() {
        component.draw();
        Delegation
    }
}

// 具体装饰器:边框
class BorderDecorator extends Decorator {
    public BorderDecorator(VisualComponent component) {
        super(component);
    }

    @Override
    public void draw() {
        super.draw();
        drawBorder();
    }

    private void drawBorder() {
        System.out.println("Drawing border");
    }
}

// 具体装饰器:滚动条
class ScrollDecorator extends Decorator {
    public ScrollDecorator(VisualComponent component) {
        super(component);
    }

    @Override
    public void draw() {
        super.draw();
        drawScrollbar();
    }

    private void drawScrollbar() {
        System.out.println("Drawing scrollbar");
    }
}

public class DecoratorDemo {
    public static void main(String[] args) {
        VisualComponent textView = new TextView();
        VisualComponent borderedTextView = new BorderDecorator(textView);
        VisualComponent scrollableBorderedTextView = new ScrollDecorator(borderedTextView);
        scrollableBorderedTextView.draw();
    }
}

在这个例子中,原始的TextView不会被修改,我们通过装饰器BorderDecorator和ScrollDecorator分别添加了边框和滚动条的功能。

代理模式:

目的:

  • 控制对其他对象的访问,相当于该对象的一个代表,客户端通常通过代理间接访问那个对象。
  • 客户端可能需要访问控制、延迟初始化、远程对象访问等。

适用场景:

  • 当需要对原有对象的操作进行控制或增强时。
  • 当需要延迟对象的创建时(虚拟代理)。
  • 当需要为远程对象提供本地代表时(远程代理)。
  • 当需要记录对象的访问日志时(保护代理)。

Java示例:

设想有一个昂贵的数据库连接类,我们不希望除非必要否则不创建连接。我们可以使用代理模式来实现懒加载。

// 接口
interface Database {
    void connect();
}

// 实际数据库连接类
class RealDatabase implements Database {
    @Override
    public void connect() {
        System.out.println("Connecting to a real database.");
    }
}

// 数据库代理类
class DatabaseProxy implements Database {
    private RealDatabase realDatabase;

    @Override
    public void connect() {
        if (realDatabase == null) {
            realDatabase = new RealDatabase();
        }
        realDatabase.connect();
    }
}

public class ProxyDemo {
    public static void main(String[] args) {
        Database db = new DatabaseProxy();
        db.connect();
        // 连接将被延迟到实际被需要的时候
    }
}

在这个例子中,DatabaseProxy作为RealDatabase的代理,它延迟了真实数据库连接对象的创建直到实际需要连接时。客户端代码使用DatabaseProxy而无需关心真正的数据库连接何时建立。

区别总结:

装饰器模式关注于在不改变接口的前提下增加对象的功能,是一种结构性扩展;而代理模式关注于控制对对象的访问,是一种控制性模式。装饰器允许递归组合,而代理通常不涉及递归。代理模式中的代理知道它正在代理的对象,而装饰器只关心接口,不需要知道具体的类。

本篇文章来源于微信公众号: 互联网面试小帮手



微信扫描下方的二维码阅读本文

此作者没有提供个人介绍
最后更新于 2024-08-02