Search code examples
javainheritancesubclasssuperclass

Cleaner way of using inheritance to separate 'static' code from 'dynamic' code


(With static and dynamic I mean the distinction whether code is susceptible to change)

I have a bit of a weird problem I'm currently stuck on. I'm writing an application which involves some complex interactions between components in which it becomes hard to keep track of the code flow. To simplify this, I'm trying to structure the code by creating 'layers', where each layer has increased functionality compared to the layer above it. Each layer is contained in a package. I'm having the following problem:

Consider the following 2 classes and their subclasses with increased functionality:

Class A:

package layer;
class A {
    B b;

    A() {
        b = new B();
    }

    void foo() {
        b.foo();
    }

    /* More A-class methods here */
}

Class B:

package layer;
class B {
    void foo() {
        // Do something
    }

    /* More B-class methods here */
}

Subclass A:

package sublayer;
class ASub extends A {

    ASub() {
        super.b = new BSub(); // This needs a cast to compile
    }
}

Subclass B:

package sublayer;
class BSub extends B {
     @Override
     void foo() {
        // Do something with more functionality
     }
}

In the end I just want to instantiate and modify classes ASub and BSub, being able to use all methods of superclasses A and B without actually needing to modify code in classes A and B itself.

If I call new ASub().foo(), I want the overridden foo() of BSub to execute instead of that of B. Ofcourse I can add a variable BSub bsub in ASub and override A's foo() method to call bsub.foo(), but this doesnt avoid the creation of the object b in the constructor of A, which seems sloppy coding. Any thoughts on this? Any comments are welcome.


Solution

  • Your question is a bit controversial. Object creation and dependency injection is the subject of a lot of discussion and a core focus of various frameworks and design patterns.

    But here is, I hope, one simple answer to your question, which isn't a general "what's the best way to create objects in Java?"

    In the code below, I move the responsibility of instantiating B to a method (instantiateB()) which is called from the A (superclass) constructor. So, when you want to subclass A, you override that method instead of overriding the constructor.

    package com.matt.tester;
    
    public class SE {
    
        static class A {
            B b;
    
            A() {
                instantiateB();
            }
    
            void instantiateB () {
                this.b = new B();
            }
    
            void foo() {
                b.foo();
            }
    
            /* More A-class methods here */
        }
    
        static class B {
            void foo() {
                System.out.println("Hellow from B.foo()!");
            }
    
            /* More B-class methods here */
        }
        static class ASub extends A {
    
            @Override
            void instantiateB() {
                this.b = new BSub();
            }
        }
        static class BSub extends B {
             @Override
             void foo() {
                System.out.println("Hellow from BSub.foo()!");
             }
        }
    
        public static void main(String[] args) {
            A a = new ASub();
            a.foo();
        }
    }