在 Java 中混合组合和继承

Mixing Composition and Inheritance in Java

是否可以在 Java 中混合组合和继承?拳头我有一些通用的类是一个HAS-A(或HAS-MANY)关系(组合)。

通用 类:

Structure、TypeA、TypeB、TypeC等,其中Structure与TypeA、TypeB、TypeC是HAS-A关系。

但是后来我也有几组sub类继承自泛型类(继承)并且处于相同的关系

套子类:

第 1 组:

Structure1、TypeA1、TypeB1、TypeC1等,其中Structure1与TypeA1、TypeB1、TypeC1是同一个HAS-A关系,就像泛型类.

并且:Structure1 extends Structure,TypeA1 extends TypeA,...,TypeC1 extends TypeC,等等

...直到第 X 组:

StructureX、TypeAX、TypeBX、TypeCX 等

每个特殊结构都有一个特定的 sub-TypeA、sub-TypeB 和 sub-TypeC 等。通用 类 定义了一些代码(属性和方法),我希望在处理特殊结构时重用这些代码。 下面的代码可以很好地解释我面临的问题。 (我不知道这是否可以通过 Java "generics" 以某种方式解决,但我认为不能。)

/***********************************************************************************************
/ Generic Structure of Structure-Class with Instances of Generic other classes (Composition)
/***********************************************************************************************/
class Structure {

    // Substructures
    TypeA instanceA;
    TypeB instanceB;

    // Defining many methods using the instances of other generic classes like TypeA
    void setGenericAttributeOfA(int value) {
        instanceA.genericAttribute = value;
    }
    void setGenericAttributeOfB(int value) {
        instanceB.genericAttribute = value;
    }
}

class TypeA {
    int genericAttribute;
}
class TypeB {
    int genericAttribute;
}

/***********************************************************************************************
/ Specific implementation of a Structure-Class with specific implementation of the other classes (Inheritance)
/***********************************************************************************************/

// In the specific implementations I want to use the generic methods, because I do not want to 
// rewrite the code for each and every specific implementation. But they should 

class Structure1 extends Structure {

    // This will create an additional attribute instanceA of specific TypeA1, so I will end up with two instances:
    // (1) TypeA super.instanceA and (2) TypeA1 this.instanceA. But what I would like is to have only
    // one instanceA of type TypeA1 that can also be used by the global methods of the generic Structure.
    TypeA1 instanceA;

    Structure1() {
        // This creates an instance of type TypeA1, but it cannot be used in the generic methods
        // because it is hold in a separate "local" variable that is not known to the generic Structure methods
        instanceA = new TypeA1();
        // This creates an instance of type TypeB1, but it cannot access the "local" specific attributes,
        // because it is hold in a varable which is statically types as TypeB
        instanceB = new TypeB1(); 
    }

    void specificMethod() {
        setGenericAttributeOfA(42); // would fail, because instanceA of type generic TypeA is null
        instanceA.specificAttribute = 13; // works only for "local" specific attributes

        setGenericAttributeOfB(42); // works only for generic attributes
        instanceB.specificAttribute = 13; // would fail, because instanceB is statically typed as generic TypeB which does not have this attribute
        ((TypeB1)instanceB).specificAttribute = 13; // works but is an ugly work-around and over-complicated if to be used many times
    }
}

class TypeA1 extends TypeA {
    int specificAttribute;
}
class TypeB1 extends TypeB {
    int specificAttribute;
}

也许Generics可以帮忙?

声明 Structure 为通用:

class Structure<T1 extends TypeA, T2 extends TypeB, T3 extends TypeC> {
    T1 instanceA;
    T2 instanceB;
    T3 instanceC;
}

并且您的特定结构将通过指定类型参数从泛型 Structure 继承:

class Structure1 extends Structure<TypeA1, TypeB1, TypeC1> {
    // here instanceA will be of type TypeA1, instanceB will be TypeB1 etc.
}

您的类型层次结构似乎不必要地复杂。围绕数据实现继承通常具有挑战性,这个场景就是一个典型的例子。

您可以考虑重新设计类型的要点:

  1. TypeTypeB 是相同的,至少就 post 而言是这样。除非这些 class 中的每一个都有特定的行为,否则您可以让它们合并:

    // Replacement for TypeA and TypeB
    class Type {
        int genericAttribute;
    }
    

    如果它们有特定的行为,您可以制作 Type 接口并考虑切换到 getter 和 setter 而不是字段(无论如何这是一个更好的主意)

  2. 之后,TypeA1TypeB1可以扩展同一个父级,Type:

    class TypeA1 extends Type {
        int specificAttribute;
    }
    class TypeB1 extends Type {
        int specificAttribute;
    }
    
  3. 关于 Structure class:你只需要这个 class 因为你想实现 "generic" 与 TypeATypeB 相关的行为。应该不再需要这种区别了,因为这两个已经合并到 Type,所以你可以省去这个 class,留下数据来处理。并且数据可以移动到 Structure1(不需要为这些字段设置一个公共的、单独的位置,因为 Structure1 无论如何都有两种具体类型):

    //this doesn't need a parent any more
    class Structure1 {
    
        TypeA1 instanceA;
        TypeB1 instanceB;
    
        Structure1() {
            instanceA = new TypeA1();
            instanceB = new TypeB1(); 
        }
    
        //This doesn't have to be implemented like this
        //I kept it to show the relation to your original
        //design. I would simply add 
        //setGenericAttribute(int) to Type
        void setGenericAttribute(Type type, int value) {
            type.genericAttribute = value;
        }
    
        void specificMethod() {
            setGenericAttribute(instanceA, 42);
            instanceA.specificAttribute = 13;
    
            setGenericAttribute(instanceB, 42);
            instanceB.specificAttribute = 13;
        }
    }
    

TL;DR 在你的类型上使用方法转发,如果这确实有效,因为事情太复杂了,那么查找 SOLID 原则并使用设计模式来制作你的类型和 class紧随其后。

  1. 为每种类型添加 getter 和 setter,并且永远不要直接访问字段。如果您打算直接访问字段,请使用内部 class。使用 getter 和 setter,您可以拥有一个 infoObject,它可以携带可以传递给您想要的特定字段的值。或者你可以传入一个数据结构,或者你使用可变参数。无论哪种方式,你都有灵活性。
  2. 我不知道你是在尝试处理结构设计问题还是行为设计​​问题,这就是你遇到问题的原因。 查找 SOLID 原则。 您的 Structure class 试图达到两个目的。您正在尝试创建一个负责创建和包含对象的数据对象,同时还尝试为对象处理数据。 您基本上需要选择两种设计模式来处理您的意图。我建议使用 Bridge and Builder 模式。 创建一个构建 Type 对象的构建器 class,然后使结构 class 仅包含顶部 class 的字段。然后结构 class 不必访问类型特定的方法或字段。构建器会,所以你需要考虑为不同类型制作构建器的最佳 SOLID 方法。您可能需要为每种类型或每个 class 构建器,具体取决于它的复杂程度,但如果您有 getter 和 setter,那也不会太糟糕。此外,您可能想考虑将工厂或抽象工厂与构建器一起使用,以便工厂确定对象的类型(以及 class child/parent 的类型),构建器将利用该信息构建一个该特定 class 的对象,其中填充了所有特定字段。
  3. 方法转发是你的朋友。你似乎已经有了这个想法,但没有尽可能多地使用它。如果你想制作通用方法来设置不同对象的值,那么 classes 有 overriden/overloaded 方法来为你完成工作。在下面的示例中更容易看到解释:

    class TypeA {
    
        int genericAttribute;
    
        setAttributes(int genericAttribute){
            this.genericAttribute = genericAttribute;
        }
    
    }
    
    class TypeB { 
    
        int genericAttribute;
    
        setAttributes(int genericAttribute){
            this.genericAttribute = genericAttribute;
         }
    }
    
    class TypeA1 extends TypeA {
    
        int specificAttribute;
    
        setAttributes(int genericAttribute, int specificAttribute){
            setAttributes(genericAttribute);
            this.specificAttribute = specificAttribute;
        }
    }
    
    class TypeB1 extends TypeB {
    
        int specificAttribute;
    
        setAttributes(int genericAttribute, int specificAttribute){
            setAttributes(genericAttribute);
            this.specificAttribute = specificAttribute;
        }
    }
    

继续这个模式,你会得到你想要的,甚至不用尝试。