简体   繁体   中英

Mixing Composition and Inheritance in Java

Is it possible to mix composition and inheritance in Java? Fist I have some generic classes is a HAS-A (or HAS-MANY) relationship (Composition).

Generic classes:

Structure, TypeA, TypeB, TypeC, etc., where Structure is in a HAS-A relationship with TypeA, TypeB and TypeC.

But then I also have several sets of subclasses which inherit from the generic classes (Inheritance) and are in the same relationship.

Sets of subclasses:

Set 1:

Structure1, TypeA1, TypeB1, TypeC1, etc., where Structure1 is in the same HAS-A relationship with TypeA1, TypeB1 and TypeC1, just like the generic classes.

And: Structure1 extends Structure, TypeA1 extends TypeA, ..., TypeC1 extends TypeC, etc.

... until Set X:

StructureX, TypeAX, TypeBX, TypeCX, etc.

Each special structure has exactly ONE specific sub-TypeA, sub-TypeB and sub-TypeC, etc. The generic classes define some code (attributes and methods) which I would like ot reuse when dealing with special structures. The problems I am facing are best explained by the code below . (I do not know if this can somehow be solved by Java "generics" but I think not.)

/***********************************************************************************************
/ 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;
}

Maybe Generics can help?

Declare Structure as generic:

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

And your specific structures will just inherit from the generic Structure by specifying type arguments:

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

Your type hierarchy seems unnecessarily complex. It's typically challenging to implement inheritance around data, and this scenario is a typical example.

Points you can consider to redesign your types:

  1. Type and TypeB are identical, at least as far as the post is concerned. Unless there's specific behavior to each of these classes, you can make them coalesce:

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

    If they have specific behavior, you can make Type an interface and consider switching to getters and setters instead of a field (which is a better idea any way)

  2. Following that, TypeA1 and TypeB1 can extend the same parent, Type :

     class TypeA1 extends Type { int specificAttribute; } class TypeB1 extends Type { int specificAttribute; } 
  3. Regarding the Structure class: you only needed this class because you wanted to implement "generic" behavior related to TypeA and TypeB . This distinction shouldn't be needed anymore as these two have been merged into Type , so you can dispense with this class, to be left with data to deal with. And data can move to Structure1 (there would be no need for a common, separate place for these fields, because Structure1 has both concrete types anyway):

     //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 Use method forwarding on your types and if that does work because things are too complex then look up SOLID principles and use design patterns to make your types and classes follow that.

  1. Add getters and setters to each type and never access fields directly. If you intend to access the fields directly use a inner class. With getters and setters you could have an infoObject that can carry values that can be passed on to the specific field that you want. Or you could pass in a data structure, or you use varargs. Either way you have flexibility.
  2. I can't tell if you're trying to handle structural design issues or behavior design issues, which is why you have are having issues. Look up SOLID principles. Your Structure class is trying to serve two purposes. You're trying to make a data object that is responsible for creating and containing objects, while also trying to process the data for the objects. You basically need to pick two design patterns to handle your intention. I would suggest the Bridge and Builder pattern. Make a builder class that builds the Type objects and then make your structural class only have fields of the top class. Then the structural class does not have to access type specific methods or fields. The builder will though, so you need to think about the best SOLID way of making a builder for the different types. You might need a builder for each type or each class depending on how complicated it is but if you have getters and setters that won't be too bad. Also you might want to think about using a factory or abstract factory with the builder so that the factory determines which type (and what class child/parent) the object will be and the builder takes that information to build an object of that specific class with all the specific fields populated.
  3. Method forwarding is your friend. You seem to have already had that idea but it wasn't used as much as you could. If you wanted to make general method that sets the values on different objects, than make the classes have overriden/overloaded methods that do the work for you. It's easier to see explain in the example below:

     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; } } 

Continue this pattern and you'll have what you want without even trying.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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