Search code examples
javainheritancegraphics2d

Graphics2D wrapper for 2d game engine


I'm trying to write a 2d game engine and I'm trying to implement a viewport system so that when I'm drawing in a certain viewport, the game coordinates will be transformed to the screen coordinates without having to manually do the transformation.

What I want to do is create a Graphics2D wrapper that adds a setViewport method.

The way I see it there are 2 options:

  1. Create a class that has an instance of a Graphics2D and has all the same methods as Graphics2D plus setViewport and just calls the respective method on the Graphics2D instance.

  2. Subclass Graphics2D and just add a setViewport method and then just cast from Graphics2D to this new class

I tried #2 because #1 seemed very impractical but ran into a ClassCastException. I'm unable to cast Graphics or Graphics2D to this new class. When I print the graphics object before the cast (either Graphics or Graphics2D), both come out as sun.java2d.SunGraphics2D.

Am I doing something fundamentally wrong trying to subclass and cast? If not, how can I fix this problem?


Solution

  • One OO design priciple is "favor composition over inheritance", this is acheived using the wrapper class or Decorator design pattern (which is in my opinion what is meant by wrapper in the course slide). So what you have done is in fact the elegant solution for a number of reasons:

    • It protects against future implementation changes in Graphics2D, if you use inheritance and by misfortune a new method is added to Graphics2D with the same signature as your new method and a different return type, your class will no longer compile. If you use the same signature and return type you would override the new method in Graphics2D which could (and has) result in several days of frustrating debugging.

    • Inheritance in this way violates encapsulation making software fragile and error-prone in the long run.

    • Using composition you are protecting your class from future changes in the class your composing with, your class would forward all method calls to it's private Graphics2D instance and handle transforming coordinates separately.

    • It also allows for easy future extension, using inheritance will tie you to the current implementation of Graphics2D potentially limiting the performance of your class.

    An example of this exists in the Java API: The Properties class extends HashTable, this illustrates improper use of inheritance because a Properties is not a HashTable, it's not meant to be used in the same way. In this case a call to Properties p.getProperty(key) might give different results than p.get(key), because the latter-case does not take defaults into account.

    The Decorator design pattern:

    public class Wrapper {
        private WrappedClass w;
    
        public Wrapper(WrappedClass w) {
           this.w = w;  
        }
    
        // Forward calls to WrappedClass methods to the private instance.
        public ReturnType example(Argument a) { return w.example(a); }
    
        // Add your methods here:
    }
    

    Although this might seem like a tedious approach it is worth it in the long run for the reasons above. Also the existance of interfaces in the Java API such as the Set interface for the HashSet above makes writing such classes easier although I don't know if such an interface exists for Graphics2D.

    sources: Effective Java 2nd Edition - Joshua Bloch