[英]Better ways to abstract subset of members in a class in Java
我有一个非常复杂的 AComplex 类,它可能包含大量的私有成员,现在我想制作该类的公共版本(称为:ASimple_V1),它只包含私有成员的一个子集,并将它们设为公开。 后来我想在以前的版本的基础上制作更多的版本(例如,ASimple_V2,基于 ASimple_V1 但从 AComplex 添加了更多)。我目前的解决方案真的很笨拙:
class AComplex{
private T1 t1;
private T2 t2;
....
}
class ASimple_V1{
public T1 t1;
public T2 t2;
}
class ASimple_V2 extends ASimple_V1{
public T3 t3;
public ASimple_V2(AComplex a){
t1 = a.t1;
t2 = a.t2;
t3 = a.t3;
}
}
我使用了一个以复杂类为参数的构造函数,仍然需要手动分配所有值。 有没有更好的方法来实现这样的目标?
如果您的问题是关于避免手动复制属性,请查看 Apache commons 中的BeanUtils 。 您必须匹配源 bean 和目标 bean 的名称,并且您的类必须确认 Javabeans 约定。
在ASimple_V2
,您所拥有的只是对“复杂对象”的引用。 像您所做的那样创建复杂类的“简单副本”并不是标准做法。 您还可以更改许多其他内容以改进类的设计。 一般来说,拥有公共字段并不是一个好的 OO 实践。 您在复杂类中拥有的可能是私有字段的 getter。
您可以通过反射this.getClass().getDeclaredFields()
解决您的问题,但我认为重组您的代码会更好。
请参阅此问题的已接受答案中 roufamatic 的评论: Java 反射:查找子类的字段
首先, private
字段通常包含特定于实现的细节,应该保持隐藏。 如果它们保持隐藏状态,您可以灵活地更改类的实现方式,而不会影响 API。 通过公开它们,它们将成为您的可导出 API 的一部分,并且您将永远使用它们。
您应该问自己的第一个问题是:“为什么这些字段不能保持私有?” 如果有令人信服的理由,您仍然应该仔细考虑是否公开披露它们; 通常,这仍然是一个坏主意(作为一个反面例子,看看java.awt.Point
类)。 您应该提供访问器方法,例如getT1
和getT2
,而不是公开字段。 这将允许您更灵活地稍后更改实现。
最后,当你构建你的类层次结构时,如何计划维护版本之间的equals
/ hashCode
契约——特别是自反关系(如v1.equals(v2)==true
但v2.equals(v1)==false
)?
我的建议:保持尽可能多的字段private
。 如果必须公开字段,请通过公共getter
方法执行此操作。 Eclipse 有一个自动化工具可以为您生成 getter 和 setter。
如果只想减少必须编写的复制代码量,可以使用反射来复制具有相同名称的成员,请参阅在 java 中相似类之间复制字段 - 堆栈内存溢出。
但是,如果您想为其他编码人员提供不同的抽象而没有复制的开销,您可以维护一个实现多个公共接口的包级实现类,这些接口提供对此类创建的同一对象的不同程度的访问。 此外,提供必要的工厂方法和转换实用程序功能。 包外的代码可以操作这些接口的对象而不是那个类,并执行必要的转换。
请参阅下面的代码:
public interface AComplex {
T1 getT1();
T2 getT2();
T3 getT3();
// ...
}
public interface ASimpleV1 {
T1 getT1();
T2 getT2();
}
public interface ASimpleV2 extends ASimpleV1 {
T3 getT3();
}
class AImpl implements AComplex, ASimpleV1, ASimpleV2 {
T1 t1;
T2 t2;
T3 t3;
@Override
public T1 getT1() {
return t1;
}
@Override
public T2 getT2() {
return t2;
}
@Override
public T3 getT3() {
return t3;
}
// ...
}
public final class As {
private As() {
}
public static ASimpleV1 newASimpleV1() {
return new AImpl();
}
public static ASimpleV1 newASimpleV1(T1 t1, T2 t2) {
return new AImpl(t1, t2, null);
}
// ...
public static ASimpleV2 ASimpleV1AsASimpleV2(ASimpleV1 aSimpleV1) {
return (ASimpleV2) aSimpleV1;
}
public static ASimpleV2 ASimpleV1AsASimpleV2(ASimpleV1 aSimpleV1, T3 t3) {
AImpl aImpl = (AImpl) aSimpleV1;
aImpl.t3 = t3;
return (ASimpleV2) aImpl;
}
public static ASimpleV1 ASimpleV2AsASimpleV1(ASimpleV1 aSimpleV2) {
return (ASimpleV1) aSimpleV2;
}
// ...
}
注意AImpl
及其成员变量是包级可见的,所以包外的代码不能直接对其成员进行操作,当然除了使用反射。
为简单起见,我只编写了 getter,您还可以根据需要添加 setter 和其他方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.