装饰模式和代理模式

   上回说到设计六原则,今天我们就拿装饰模式和代理模式来验证一下。

   装饰模式,官方是这么解释的:动态的给一个对象添加额外的功能。说的很清楚吧,就好比国产凌凌漆的皮鞋,给它装上电池和风扇呢,它就变成了电吹风!

   我们来看看装饰模式的UML图:

   注意接口这个恶魔出现了,它躲在众多实现类身后向你伸出精神控制的触手,刺入你的大脑,在你的思想里植入一种叫做“面向接口编程”的病毒。从此你慢慢变得双眼无神、毛发脱落、肤色苍白,最终将彻底蜕变成一种叫做“架构尸”的半死不活的生物。

   事到如今什么都拯救不了你啦,所以我们还是接着看装饰模式。ConcreteComponent是要扩展的类,它实现了接口Component。Decorator是扩展ConcreteComponent的,它也实现Component接口。Decorator怎样给ConcreteComponent添加额外功能呢?首先Decorator包含一个Component的对象,然后它的sampleOperation方法可以这样实现:

@Override

public void sampleOperation() {

     System.out.println(“我要额外干点啥!”);

     component.sampleOperation();

     System.out.println(“僵尸吃掉了你的脑子!咦,我为什么要说这个?”);

}

   假设现在你中的“设计魔尸”毒还不够深,大脑还有自主思考的能力,那么你就要问了:“为了加两行代码弄出个类来了,何必这么麻烦?我直接改ConcreteComponent的sampleOperation方法!”

   好的!我就是要你这样想!你已堕入彀中,我这早就准备好六大原则准备糊你熊脸呢!还记得开闭原则说的什么:对扩展开放,对修改关闭!嘿嘿,少年,乖乖献出你的膝盖吧。

   这里对开闭原则补充两句:对修改关闭,不是说不让修改代码,还没经过测试和发布的代码当然要不断修改,不然发现bug怎么办。开闭原则是针对已release的代码升级来说的,尽量采用装饰模式来扩展原有类的功能。即使是已release的代码,也不是绝对不能修改的,当年JDK为了泛型,把全部Collection实现都重写了一遍。

   这时又一位少年跳出来喊道:“老狗休要得意,我继承ConcreteComponent并重写sampleOperation方法难道不可?”老狗狞笑着从背后取出合成复用原则道:“少年还是too simple,too naïve 。难道忘了还有这个宝贝吗?”

   所以血气方刚的少年们啊,不管你有多少剑走偏锋的招式,都不要忘了还有六大原则这面金刚盾。二十三种设计模式一定是满足六原则的,少年你可以放心使用。那是不是不能有超出这二十三中模式之外的设计出现?绝对不是,编程还有一条基本原则:用最简单的方法实现。但你在这样做的时候,一定要再三的思考和检验,是否违反了某条原则。

   本节我们还要讲一个与装饰模式很相识的模式:代理模式。下面来看代理模式的UML图:

   眼尖的少年已经跳起来喊了:“卧槽怎么跟装饰模式一模一样!”对,你没看错,确实两种模式非常相识。都是实现同一个接口的两个类,第二个类对第一个类的接口方法有所扩展。

   不过细看两者还是有些细微差别的:装饰模式的Decorator持有的不是ConcreteComponent类的对象,而是Component接口。而ProxyObject持有的就是RealObject类的对象。那么Decorator是如何获得ConcreteComponent类的对象呢,一般是在构造方法中传入,像这样:

public Decorator(Component component) {

     this.component= component;

}

Component component = new ConcreteComponent();

Decorator decorator = newDecorator(component);

   那么除了具体实现细节略有不同,装饰模式和代理模式到底有什么根本性的区别呢?或者说装饰模式为什么不能完全取代代理模式呢?说实话老狗我也没想通这里面的缘由,只能说是这两种模式的目的不同:装饰模式是为了扩展一个类的功能,代理模式是提供对一个类的间接访问。

   最后提一句,Java中的各种InputStream类,是典型的装饰模式的实现,有兴趣的同学可以去看看Java的I/O库的源码。

<think>好的,我现在需要回答用户关于Java装饰模式代理模式的区别及用法对比的问题。用户已经提供了一些相关的引用资料,我需要结合这些资料以及之前的知识来组织回答。 首先,我需要回顾装饰模式代理模式的基本概念。装饰模式用于动态地给对象添加职责,而代理模式则是控制对对象的访问。根据引用[1],这两个模式都基于组合,结构上相似,容易混淆。引用[3][4]提到,装饰模式强调功能扩展,代理模式强调访问控制。此外,代理模式可能在内部创建目标对象,而装饰模式通过构造函数传入目标对象。 接下来,我需要整理它们的区别。根据引用,主要区别可能在于目的、对象创建方式、使用阶段、透明性、接口方法以及应用场景等方面。例如,装饰模式是透明的,可以叠加使用,而代理模式可能隐藏目标对象,并且可能仅代理部分方法。 然后,我需要考虑Java中的应用实例。用户提供的示例中,装饰模式用于I/O流,如FilterInputStream,而代理模式如Spring AOP的动态代理。需要将这些例子融入回答中,以具体说明区别。 另外,用户的问题还提到了用法对比,所以需要分别说明何时使用装饰模式,何时使用代理模式,并给出实际场景。例如,装饰模式适用于需要动态添加功能的场合,代理模式适用于访问控制、延迟加载等。 最后,需要确保回答结构清晰,分点明确,并引用提供的参考资料来支持观点。同时,要避免重复,并确保语言准确,符合用户要求的格式,如使用中文,正确引用标识等。</think>在Java中,装饰模式代理模式都通过组合方式操作目标对象,但二者的设计目的实现细节存在本质差异。以下是两者的详细对比分析: --- ### 一、核心差异对比表 | 维度 | 装饰模式(Decorator) | 代理模式(Proxy) | |------------------|---------------------------------------------|-------------------------------------------| | **核心目的** | 动态添加功能(增强型扩展)[^3] | 控制对象访问(保护/延迟/远程访问等)[^4] | | **目标对象创建** | 由客户端传入(构造方法注入)[^3] | 通常由代理类内部创建(隐藏目标对象)[^3] | | **使用阶段** | 运行时动态叠加装饰 | 编译期或运行时(动态代理)确定代理关系 | | **透明性** | 对客户端透明(接口一致)[^1] | 可能不透明(隐藏目标对象状态)[^4] | | **接口方法** | 通常实现目标对象完整接口 | 可能仅代理部分方法 | | **典型应用** | Java I/O流装饰器、GUI组件增强[^1] | Spring AOP、远程方法调用、虚拟代理[^2] | --- ### 二、代码实现差异示例 #### 1. 装饰模式(以I/O流为例) ```java // 组件接口 public interface DataSource { void writeData(String data); } // 基础组件 class FileDataSource implements DataSource { public void writeData(String data) { System.out.println("写入文件: " + data); } } // 装饰器基类 abstract class DataSourceDecorator implements DataSource { protected DataSource wrappee; public DataSourceDecorator(DataSource source) { this.wrappee = source; } } // 具体装饰器:加密功能 class EncryptionDecorator extends DataSourceDecorator { public EncryptionDecorator(DataSource source) { super(source); } @Override public void writeData(String data) { wrappee.writeData(encrypt(data)); } private String encrypt(String data) { return "加密后的" + data; } } // 使用方式:动态叠加功能 DataSource source = new EncryptionDecorator(new FileDataSource()); source.writeData("敏感数据"); // 输出:写入文件: 加密后的敏感数据 ``` #### 2. 代理模式(以虚拟代理为例) ```java interface Image { void display(); } // 真实对象(延迟加载) class RealImage implements Image { private String filename; public RealImage(String filename) { this.filename = filename; loadFromDisk(); } private void loadFromDisk() { System.out.println("加载大文件: " + filename); } public void display() { System.out.println("显示图片: " + filename); } } // 代理类(控制访问) 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(); } } // 使用方式:客户端无感知 Image image = new ProxyImage("1GB_Photo.jpg"); image.display(); // 首次调用时才加载大文件 ``` --- ### 三、关键差异点详解 1. **目标对象控制权** - 装饰器:客户端显式传递被装饰对象,可多层嵌套(如`new A(new B(new C()))`) - 代理器:代理类内部管理目标对象生命周期,客户端无法直接访问真实对象[^3] 2. **设计意图差异** - 装饰模式关注**功能增强**(如I/O流的缓冲/加密装饰)[^1] - 代理模式关注**访问控制**(如Spring AOP的权限校验、日志记录)[^2] 3. **方法覆盖范围** - 装饰器通常实现目标对象完整接口(保证透明性) - 代理器可能仅实现部分方法(如远程代理只代理远程调用相关方法)[^4] 4. **运行时行为** ```mermaid graph LR A[客户端] --> B[装饰器] B --> C[被装饰对象] D[客户端] --> E[代理] E --> F[真实对象] ``` --- ### 四、应用场景选择指南 | **场景特征** | 推荐模式 | |-------------------------------|---------------| | 需要动态叠加多个功能 | 装饰模式 | | 需要控制对象创建/访问时机 | 代理模式 | | 需要拦截特定方法(如AOP) | 动态代理[^2] | | 需要保持接口完全一致 | 装饰模式[^1] | | 需要隐藏目标对象细节 | 代理模式[^4] | --- ### 五、常见混淆场景解析 1. **日志记录功能** - 代理模式:记录方法调用信息(如Spring AOP) - 装饰模式:在原有功能基础上增加日志格式处理 2. **缓存实现** - 代理模式:实现缓存代理(如MyBatis的Mapper代理) - 装饰模式:给数据源添加缓存层(如装饰数据库连接) --- 通过理解二者的核心差异,可以更准确地选择模式。在Java生态中,装饰模式常见于I/O流处理(如`BufferedInputStream`),而代理模式广泛应用于框架级控制(如Spring的声明式事务管理)[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱学习饼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值