装饰器模式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
相关产品推荐
相关产品推荐

