本文介绍如何通过装饰器模式(decorator pattern)在无法修改现有子类(如 cow、pig)的情况下,为 animal 继承体系注入统一的增强逻辑(如自定义 die()、droploot()),同时保留原有子类特有行为(如 ai()、moo())。
本文介绍如何通过装饰器模式(decorator pattern)在无法修改现有子类(如 cow、pig)的情况下,为 animal 继承体系注入统一的增强逻辑(如自定义 die()、droploot()),同时保留原有子类特有行为(如 ai()、moo())。
在面向对象设计中,当需要为已有类层次结构“横向”添加职责,又受限于无法修改原始子类(Cow、Pig)时,继承(extends)和泛型类型参数化继承(如 class Pet
装饰器模式的核心思想是:让新类(Pet)继承抽象基类(Animal)并持有一个具体子类实例(Cow 或 Pig)作为委托对象。它对外表现为一个 Animal,对内按需转发或增强方法调用,从而实现“逻辑复用 + 行为定制”的双重目标。
以下是完整实现示例:
public abstract class Animal {
public abstract void eat();
public abstract void ai();
public abstract void die();
public abstract void dropLoot();
}
// 假设 Cow 和 Pig 已固定,不可修改
public class Cow extends Animal {
@Override public void eat() { System.out.println("Cow eats grass"); }
@Override public void ai() { System.out.println("Cow performs grazing AI"); }
@Override public void die() { System.out.println("Cow collapses"); }
@Override public void dropLoot() { System.out.println("Cow drops leather and beef"); }
public void moo() { System.out.println("Moo!"); } // Cow 特有方法,Pet 无法直接调用
}
public class Pig extends Animal {
@Override public void eat() { System.out.println("Pig eats slop"); }
@Override public void ai() { System.out.println("Pig performs rooting AI"); }
@Override public void die() { System.out.println("Pig falls asleep forever"); }
@Override public void dropLoot() { System.out.println("Pig drops pork and truffle"); }
}
// ✅ 关键:Pet 是 Animal 的子类,内部包装一个 Animal 实例
public class Pet extends Animal {
private final Animal decorated;
public Pet(Animal animal) {
this.decorated = Objects.requireNonNull(animal, "Wrapped animal cannot be null");
}
// 直接委托:保持原行为(如 AI)
@Override
public void ai() {
decorated.ai();
}
// 增强委托:前置/后置逻辑 + 原调用
@Override
public void die() {
System.out.println("[Pet] Performing last rites...");
decorated.die(); // 调用 Cow.die() 或 Pig.die()
System.out.println("[Pet] Pet spirit ascends ?");
}
// 完全覆盖:完全替换原实现(如 loot 逻辑)
@Override
public void dropLoot() {
System.out.println("[Pet] Dropping heart-shaped loot!");
// 注意:此处不调用 decorated.dropLoot(),实现彻底替换
}
// 其他方法同理(eat、...),按需选择 delegate / enhance / override
@Override
public void eat() {
System.out.println("[Pet] Eating with extra affection ?");
decorated.eat();
}
}使用方式简洁直观:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
立即学习“Java免费学习笔记(深入)”;
Animal petCow = new Pet(new Cow()); Animal petPig = new Pet(new Pig()); petCow.ai(); // → "Cow performs grazing AI" (原逻辑) petCow.eat(); // → "[Pet] Eating with extra affection ?" + "Cow eats grass" petCow.die(); // → 自定义前/后处理 + "Cow collapses" + "Pet spirit ascends ?" petCow.dropLoot(); // → "[Pet] Dropping heart-shaped loot!"(完全替代) // ✅ 类型安全:所有 petX 都是 Animal,可无缝接入现有 Animal 接口系统 List<Animal> pets = Arrays.asList(petCow, petPig); pets.forEach(Animal::dropLoot); // 统一触发宠物化掉落逻辑
⚠️ 注意事项与最佳实践:
- 不可访问子类特有方法:Pet 只能重写 Animal 中声明的方法(如 moo() 无法被 Pet 调用或代理),这是多态契约的体现;若需访问,应将方法上提至 Animal 抽象层(如 makeSound()),或采用组合+适配器模式。
- 避免循环委托:确保 decorated 是具体子类(Cow/Pig)而非另一个 Pet,否则可能引发无限递归(除非你明确设计为装饰器链)。
- final + 构造注入:将 decorated 设为 final 并通过构造函数传入,保障不可变性与线程安全。
- 空值防护:使用 Objects.requireNonNull 防止空指针,提升健壮性。
总结来说,装饰器模式以组合代替继承,在零侵入原始代码的前提下,实现了职责的灵活叠加。它不仅是本问题的标准解法,更是 Java I/O(BufferedInputStream)、Swing(JScrollPane)等核心库广泛验证的成熟范式——当你需要“给对象穿衣服”,就别再试图改它的基因了。

















