Search code examples
javacompositionaggregationliskov-substitution-principle

Is there a way to use the Delegation design pattern without losing substitutability in Java?


This question refers to the Delegation design pattern found here.

I have a number of interfaces for my game engine representing various entities:

  • Player
  • Vehicle
  • Mesh
  • etc.

and each one of these can be rendered, so they implement the Renderable interface which contains the method render().

METHOD 1

Using delegation, an example looks like this:

public interface Vehicle {

    public void drive();

}

public class Car implements Vehicle {

    public Renderable renderable;

    @Override
    public void drive() { // ... }

}

Whenever I want to render a car, I just call car.renderable.render();.

The problem with this method is that I cannot create a List and iterate through it.

METHOD 2

To solve this, I could have Vehicle extend Renderable:

public interface Vehicle extends Renderable {

    public void drive();

}

but the problem with this is that if I define Car, Bicycle, Truck, Tank, etc. each of these classes will have to fill in the code for render() (which will probably be identical).

Is there a way to maintain the benefits of extending Renderable in my Vehicle interface without being forced to define render() in all of my concrete classes implementing Vehicle?


Solution

  • Regarding my comment,

    Does each class that holds a Renderable have a public Renderable getRenderable() method? And if so, couldn't this be made into an interface allowing you to hold a collection of these beasts?

    I was meaning something like this:

    interface Renderable {
       void render();
    }
    
    interface RenderDelegator {
       Renderable getRenderable();
       void setRenderable(Renderable renderable);
    }
    
    abstract class Vehicle implements RenderDelegator {
       private Renderable renderable;
    
       @Override
       public Renderable getRenderable() {
          return renderable;
       }
    
       @Override
       public void setRenderable(Renderable renderable) {
          this.renderable = renderable;
       }
    
       public abstract void drive();
    }
    
    class Car extends Vehicle {
       @Override
       public void drive() {
          // TODO finish!
       }
    
    }
    

    And nix that suggestion about implementing Iterable. I'm not sure what I was thinking.