java远离子对象:从继承中删除受保护的字段
本着精心设计的OO的精神,我正在扩展的某个类标记了它的一个受保护的领域。这个类还慷慨地提供了一个公共setter,但没有getter
我用一个基类扩展这个类,这个基类又由几个孩子扩展。如何限制我的孩子对受保护变量的访问,同时仍然能够私下操作和公开设置它
见下例:
public abstract class ThirdPartyClass {
protected Map propertyMap;
public void setPropertyMap(Map propertyMap){
this.propertyMap= propertyMap;
}
// Other methods that use propertyMap.
}
public abstract class MyBaseClass extends ThirdPartyClass{
// Accessor methods for entries in propertyMap.
public getFoo(){
propertyMap.get("Foo");
}
public getBar(){
propertyMap.get("Bar");
}
// etc...
}
public class OneOfManyChildren extends MyBaseClass {
// Should only access propertyMap via methods in MyBaseClass.
}
我已经发现我可以通过在MyBaseClass
中设置字段private final
来撤销访问权。然而,这也妨碍了使用超类提供的setter
我可以用下面的“聪明”来绕过这个限制,但它也会导致维护同一地图的两个副本以及在每个元素上复制的O(n)操作
public abstract class MyBaseClass extends ThirdPartyClass{
private final Map propertyMap = new HashMap(); // Revokes access for children.
/** Sets parent & grandparent maps. */
@Override
public final void setPropertyMap(Map propertyMap){
super.setPropertyMap(propertyMap);
this.propertyMap.clear();
this.propertyMap.putAll(propertyMap);
}
}
有没有更好的方法来实现这一点
注意:这只是真正问题的一个例子:如何在不维护多个副本的情况下限制对受保护字段的访问强>
注意:我还知道,如果该字段首先使用protected
访问器生成private
,这将不是问题。遗憾的是,我无法控制这一切
注意:需要关系(继承)
注意:这很容易应用于任何集合、DTO或复杂对象
对那些误解问题的人的隐喻:
这类似于祖父母有一个饼干罐,所有家庭成员和家中其他任何人都可以使用(受保护)。一位家长带着年幼的孩子走进房子,出于他们自己的原因,希望防止他们的孩子挖掘饼干罐而令人作呕。相反,孩子应该向父母要一块巧克力饼干,然后看着它神奇地出现;糖饼干或奥利奥也是如此。他们永远不需要知道cookies都存储在同一个罐子里,或者是否有一个罐子(黑匣子)。如果罐子属于父母,如果可以说服祖父母收起饼干,或者如果祖父母自己不需要接触饼干,就可以很容易地做到这一点。除了创建和维护两个相同的jar之外,如何限制儿童访问,而不妨碍家长&;祖父母
# 1 楼答案
Laf已经指出,通过将子类转换为第三方类,可以很容易地绕过此解决方案。但是,如果这对您来说没有问题,并且您只想对子类隐藏受保护的父映射,而不维护映射的两个副本,那么您可以尝试以下方法:
还要注意的是,如果父地图受到保护与否,这其实并不重要。如果确实希望通过子类访问此字段,则始终可以使用反射来访问该字段:
因此,它实际上归结为一个问题,为什么您不希望子类访问该字段:
1)这是否只是为了方便起见,因此很快就会清楚您的api应该如何使用那么,简单地对子类隐藏字段也许是好的
2)是否出于安全原因?然后,您肯定应该搜索另一个解决方案,并使用一个特殊的安全管理器,它还禁止通过反射访问私有字段
也就是说,您可以尝试另一种设计:不扩展第三方类,而是保留该类的最终内部实例,并提供对内部类的公共访问,如下所示:
然后,当您需要使用第三方类的实例访问第三方库时,可以执行以下操作:
# 2 楼答案
可悲的是,你无能为力。如果该字段为
protected
,则它要么是一个有意识的设计决策(在我看来是一个错误的决策),要么是一个错误。无论哪种方式,您现在对此都无能为力,因为您无法降低字段的可访问性事实并非如此。您所做的就是所谓的变量隐藏。由于在子类中使用相同的变量名,因此对
propertyMap
变量的引用现在指向MyBaseClass
中的私有变量。但是,您可以非常轻松地绕过隐藏此变量,如下代码所示:因此,您无法“撤销”对超级类
ThirdPartyClass
中受保护类成员的访问。您没有太多选择:如果您的子类不需要知道
MyBaseClass
上面的类层次结构(即,它们根本不会引用ThirdPartyClass
),并且如果您不需要它们成为ThirdPartyClass
的子类,那么您可以将MyBaseClass
创建为一个不从ThirdPartyClass
扩展的类。相反,MyBaseClass
将持有ThirdPartyClass
的实例,并将所有调用委托给该对象。通过这种方式,您可以控制ThirdPartyClass
的API的哪一部分真正公开给您的子类如果需要从
MyBaseClass
直接访问ThirdPartyClass
的propertyMap
成员,则可以定义私有内部类并使用它访问该成员:如果第一个解决方案不适用于您的案例,那么您应该准确地记录
MyBaseClass
的子类可以做什么,不应该做什么,并希望它们尊重您的文档中描述的契约# 3 楼答案
所以你想让公众拥有比你更多的权利?你不能这么做,因为他们总是可以调用公共方法。。。这是公开的
# 4 楼答案
创建另一个名为
propertyMap
的受保护变量怎么样?如果你的孩子们上课的话,那应该超过阴影。您还可以实现它,以便对其调用任何方法都会导致异常但是,由于访问器方法是在基类中定义的,因此它们不会看到第二个阴影版本,并且仍然会对其进行适当的设置
# 5 楼答案
好的,作弊模式在: 覆盖反公共setter并将映射实现更改为MyBaseClass的内部类怎么样。此实现可能会在您不希望您的孩子访问的map的所有方法上引发异常,并且您的MyBaseClass可以通过使用您的map实现的内部方法公开他们应该使用的方法。。。 仍然需要解决第三方方法如何访问这些属性,但是您可以在使用MyBaseClass之前强制代码调用finalizationMethod。。。我只是在这里跳
编辑
像这样:
# 6 楼答案
这对您来说可能是不可能的,但您是否可以从ThirdPartyClass派生一个接口并使ThirdPartyClass实现它
然后让MyBaseClass充当装饰器,通过将接口委托给私有成员ThirdPartyClassImpl来实现接口
即
等