You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

装饰器模式OOP实现疑问:为何具体装饰器构造函数需重复父类构造逻辑?

为什么装饰器子类必须重复初始化父类已处理的字段?

这问题的核心其实是字段的隐藏(Field Hiding),你不小心在子类里创建了一个和父类同名但完全独立的字段,导致父类的初始化根本没作用到你子类用的那个字段上!

咱们一步步拆解你的初始代码问题:

  • 父类Decorator里有一个private的Component component字段,它的构造函数初始化的是这个父类自己的字段。
  • 但你在ConcreteDecorator1里又定义了一个private的Component component字段——这两个字段是完全分开的!父类的构造函数只会初始化父类的那个component,子类的这个字段从始至终都是null,所以你调用component.Operation()时自然会抛异常。

你以为父类的初始化能覆盖子类的字段?其实不行,因为private字段是完全封装在父类内部的,子类根本访问不到它;而你在子类里重新定义的同名字段,相当于在子类的作用域里“隐藏”了父类的那个字段,两者没有任何关联。

正确的解决思路

要解决这个问题,你不需要在子类里重复定义字段,而是让父类把component字段的访问权限设为protected,这样子类就能直接继承并使用这个字段,不用自己再搞一个:

class Decorator : Component {
    // 改成protected,让子类可以访问
    protected Component component;
    public Decorator(Component component) {
        this.component = component;
    }
}

class ConcreteDecorator1 : Decorator {
    // 删掉子类自己的component字段!
    public ConcreteDecorator1(Component component) : base(component) {
        // 这里不用再赋值component了,父类已经搞定
    }
    public override int Operation() {
        myField = 100;
        // 直接用父类的protected字段component
        myField = this.myField + component.Operation();
        return myField;
    }
}

这样改完,子类调用的component就是父类构造函数初始化的那个,再也不会出现null的情况,也不需要重复写赋值代码。

对你的变体代码的点评

  • 变体2:把父类的component改成public属性确实能工作,但破坏了封装性——装饰器内部的组件应该是隐藏实现细节的,不应该暴露成public。
  • 变体3:这是非常标准的装饰器模式实现!父类Decorator把component设为protected,实现了Operation的核心逻辑(叠加字段值),子类只需要在构造函数里设置自己的myField值,完美符合职责分离的设计原则,是最推荐的写法。

总结一下:不要在子类中重复定义和父类同名的私有字段,而是通过protected权限让子类继承父类的字段访问权,这样父类的构造函数初始化就能被子类复用,既避免了重复代码,也不会出现奇怪的null异常。

内容的提问来源于stack exchange,提问作者Sami

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 06:02:38