繁体   English   中英

在 Java 中抽象类中成员子集的更好方法

[英]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类)。 您应该提供访问器方法,例如getT1getT2 ,而不是公开字段。 这将允许您更灵活地稍后更改实现。

最后,当你构建你的类层次结构时,如何计划维护版本之间的equals / hashCode契约——特别是自反关系(如v1.equals(v2)==truev2.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM