Delegation with Graphics2D (abstract class) and "by": Sanity check


#1

Hello,

I am trying to figure out the best way to provide an interface that hides the implementation details, so it will be easier to implement another alternative in the future.

Inheritance is a possibility, but since Graphics2D is an abstract class, it would be a lot of work.

Composition/delegation seems like a good fit, but I cannot figure out how to use the nifty “by” delegation in Kotlin because Graphics2D is an abstract class. The best solution that I could figure out is to manually implement delegation, such as the following… Sanity check… Is there any way to use “by” in this situation?

Thank you for your help and time!

interface IGraphics {
    fun drawRect(x: Int, y: Int, width: Int, height: Int)
}

import java.awt.Graphics2D
import java.awt.image.BufferedImage

class Graphics(val width: Int, val height: Int) : IGraphics {
    private val image = BufferedImage(width, height, 0)
    private val graphics: Graphics2D = image.createGraphics()

    override fun drawRect(x: Int, y: Int, width: Int, height: Int) {
        graphics.drawRect(x, y, width, height)
    }
}

#2

In order to use by class delegation, you need an instance of IGraphics (by the way, Kotlin/Java does not favor Hungarian notation much). So it would work like

class Graphics(val graphics: IGraphics, val width: Int, val height: Int) : IGraphics by graphics {
...
}

You can then make the constructor private and add additional constructor which generates your graphics.

If you have only several methods, it does not worth the effort, but if you have a lot of methods, it would make sense to do it this way.


#3

If you want to hide the actual graphics (and don’t want it to be determined by the constructor caller) you can create a private primary constructor that takes a graphics2d parameter, and a public secondary constructor that creates the object (and passes in the parameter).


#4

Thank you very much for showing me your idea! I am going to experiment with it this evening!

I’ve noticed, too, that Hungarian notation is not used as often, and the preference is to name the interface something like “Graphics” and its implementation as “GraphicsImpl”. I am working with a few .NET developers on my project, so we had to negotiate a bit on naming. I guess that a rose is a rose by any name, right? :stuck_out_tongue: Thank you, again, for your help!

Mike


#5

This is an interesting idea, thank you! Your idea and Darksnake’s suggestion are great alternatives for me to use in this situation. I will give them a whirl in a few hours. Thank you for your help!

Mike


#6

Regarding naming:

Why do we use interfaces? It’s so that we can provide multiple implementations. Even if you’re only planning a single implementation for now, you’re deliberately leaving open the possibility of others in future. (If there could only ever be one implementation, there wouldn’t be much point in having an interface for it.)

That’s why there’s not much point in calling an implementation <MyInterface>Impl. When you add a second implementation, what will you call that, and how will people know which one to use? The name will become meaningless.

For the same reason, the I prefix doesn’t help much, either; the first implementation can strip the I, but it’ll just be confusing once you add a second.

Instead, the most common Java/Kotlin convention is <Implementation><Interface>. For example: ArrayList is an implementation of the List interface that uses an array. HashMap implements the Map interface using a hash table. GradientPaint implements the Paint interface by drawing a linear colour gradient. And so on.

That clearly indicates the relationship between interfaces and their implementations, and forces you to think about what characterises an implementation and how it differs from any others. (And by keeping interface names short, it encourages you to program to the interface and not to a particular implementation, which keeps code simple and flexible.)

(If you really can’t think how to describe your implementation, something like Simple<Interface> or Default<Interface> may be a reasonable bet.)

This particular case is awkward, because there’s already an AWT class called Graphics, so you can’t use that. Instead, I’d suggest picking something new for your interface, perhaps something like Drawable. Then your first implementation could be called GraphicsDrawable, because it’s implementing it using a Graphics object.

As you say, the code can still work correctly even with bad names — but good names can make it so much easier to read and understand the code, and avoid confusion and subtle errors in future. It’s well worth the time and effort to pick clear, helpful names.


#7

Hi Gidds,

Thank you for the great information about idiomatic naming for Kotlin development. Based on your comments about an interface having multiple implementations as an application evolves, I have sent a proposal to rename several of our project’s files and classes.

Thank you again for the great description and suggestion!
Mike