Search code examples
javainheritancedesign-patternscomposite

How to inherit state from multiple classes?


I have a composite like pattern in where there is different functionality for the roots of the trees, branches and leafs. The problem is that there is a shared state between both Root with Branch and Branch with Leaf. I'm searching for the cleanest solution which prevents code duplication.

Diagram

The functionality among root, branch and leaf all relate to the same set of methods (though leaf has different signatures).

  • Embeddable interface implicates that the implementation has a parent and some mutable properties.
  • Composite has Embeddable children and provides abstract functionality methods for which Root and Branch have their separate implementations.
  • Root is the actual executor of most functionality.
  • Branch will most often - but not always - pass information to its parent with modified parameters.
  • Leaf can appear both on the root and branches and has fewer functionality methods with smaller method signatures. Will always pass its calls to its parent with extra parameters. It is stored explicitly in every Composite where it is created and loaded lazily.

Shared state

  • Root and Branch's shared state is in this case solved by Composite.
  • The Embeddable interface enforces that Branch and Leaf provide its behavior.

The problem the implementation is the same for Branch and Leaf and implementing it would cause duplicate code. It's impossible to make Embeddable an abstract class because Branch already extends Composite.

I'm searching for a pattern or general solution so that Branch can share state with Embeddable and Composite at the same time. Basically a multiple-inheritance workaround.

Implementation

This design is in practice used to structure a 3D block environment. Root being infinite in space, branches are a subsection with an offset in the space of its parent and leaves represent 1 block relative to its parent offset. Some functionality for root and branch thus requires coordinates as parameters, whereas leaf won't require those, since it's already at 1 coordinate.

public abstract class Container{
    private List<Embeddable> children;
    private WeakHashMap<Integer, Leaf> leafs;

    public abstract boolean setBlock(short x, short y, short z, byte id, byte data, SetBlockStrategy setBlockStrategy);
    public abstract void tick();
    public abstract void activate(short x, short y, short z);

    protected final Leaf getLeaf(short x, short y, short z) { ... }

    protected void addChild(short x, short y, short z, Embeddable child) { ... }
}

public interface Embeddable {
    void setParent(Container parent, short x, short y, short z);
    void clearParent();
    Container getParent();
}

public class Root extends Composite {
    private RootSpecificObject rootSpecificParameter;

    public boolean setBlock(short x, short y, short z, byte id, byte data, SetBlockStrategy setBlockStrategy) {
        controller.doStuff();
    }

    public void tick() {
        controller.doStuff();
    }

    public void activate(short x, short y, short z) {
        controller.doStuff();
    }
}

public class Branch extends Composite implements Embeddable {
    private BranchSpecificObject branchSpecificParameter;

    // Duplicate code
    private short xOffset, yOffset, zOffset;
    private Container parent;

    public boolean setBlock(short x, short y, short z, byte id, byte data, SetBlockStrategy sbs) {
        parent.setBlock(x + xOffset, y + yOffset, z + zOffset, id, data, sbs);
    }

    public void tick() {
        controller.doStuff(xOffset, yOffset, zOffset, ...);
    }

    public void activate(short x, short y, short z) {
        parent.activate(x + xOffset, y + yOffset, z + zOffset);
    }

    // Duplicate Embeddable implementation
}

public class Leaf implements Embeddable {
    private byte id, data;
    // Duplicate code
    private short xOffset, yOffset, zOffset;
    private Container parent;

    public boolean setBlock(byte id, byte data, SetBlockStrategy sbs) {
        return parent.setBlock(xOffset, yOffset, zOffset, id, data, sbs);
    }
    public void activate(short x, short y, short z) {
        parent.activate(xOffset, yOffset, zOffset, id, data, sbs);
    }

    // Duplicate Embeddable implementation
}

Solution

  • It's not clear what exact code is duplicated, but the common way for such problem is to delegate state and behavior to another class.

    Remember there's always a trade-off between readability and easiness to maintain such code. Think twice now that you really need to avoid code duplication.

    Short example for your code

    public class Branch extends Composite implements Embeddable {
        EmbeddableDelegate delegate;
    }
    
    public class Leaf implements Embeddable {
        EmbeddableDelegate delegate;
    }
    
    public class EmbeddableDelegate [implements Embeddable] {
        private short xOffset, yOffset, zOffset;
        private Container parent;