GitHub Decentralized Repo
GitHub Centralized Repo

As promised, we’ll discuss by delegation, but I now think that it isn’t going to be as useful as I had hoped. I could be wrong, and — oddly enough — hope that I am.

Delegation

When an object has a responsibility that is large enough, or different enough from its other responsibilities, we will often create another object for the first one to have, and “delegate” the responsibility to that new object. As an example, the various objects in Asteroids, Asteroid, Missile, Saucer, and Ship, all use a Collision object to determine whether another object is colliding with them. The “delegate” the collision determination to that object.

Typically when we do that, in the first object we have to create the delegate, or be provided it, and we have to explicitly send it the message. So if in our class Foo we have delegated bah to a BahDoer, our code would perhaps look something like this:

class Foo
    fun bah() {
    	this.bahDoer.bah()
    }

So there is a savings, if the bah method is large, or if it is used often, but we still have that explicit implementation of bah in our Bah class, short though it is.

Kotlin offers a way to do some delegation without the need to provide the forwarding method, using its by keyword. Let’s work through the example I created last night:

interface DoSomething {
    fun doingIt(input: Int): Int
    fun doingOtherThing(input: Int) :Int
}

We have some interface that a number of our classes will have to implement. Those classes need to provide both those methods. But suppose that often, they only want to implement one of them, and sometimes they can even use some default implementations.

We begin by creating a concrete class that implements the interface, providing default implementations of the functions:

class BasicDoer: DoSomething {
    override fun doingIt(input: Int): Int = input
    override fun doingOtherThing(input: Int): Int = input + 7
}

Above. we have a class that provides default implementations for both methods. This will be our delegate class.

Now we come to a class we actually need, that happens to be able to use both those default methods. In Kotlin, all we have to say is this:

class OneDoer: DoSomething by BasicDoer() {
	fun somethingOneDoerDoes() ...
}

OneDoer does not override the doingIt and doingOtherThing functions, because it said by BasicDoer(), which instantiates a Basic Doer, tucks it away in some magical place, and automatically calls it when OneDoer is asked to execute doingIt or doingOtherThing.

With just that one phrase, we delegate the DoSomething interface problem off to our BasicDoer.

But we can override if we want to:

class TwoDoer: DoSomething by BasicDoer() {
    override fun doingIt(input: Int): Int = 3*input
}

In TwoDoer above, we override just one of the two functions, doingIt, and accept the default for the other. So our classes can implement the overrides that need to be different, while deferring the others back to our instance of BasicDoer.

Here are the tests for the code above:

class DelegationTests {
    @Test
    fun `doers do it`() {
        assertThat(OneDoer().doingIt(2)).isEqualTo(2)
        assertThat(TwoDoer().doingIt(2)).isEqualTo(6)
        assertThat(OneDoer().doingOtherThing(1)).isEqualTo(8)
        assertThat(OneDoer().doingOtherThing(-5)).isEqualTo(2)
    }
}

They run. All this is the good news.

Our Problem

It’s not really much of a problem, but I was thinking that there is a lot of duplication in the area of interactWith and interact, the Collider interface:

interface Collider {
    val position: Point
    val killRadius: Double
    fun interact(asteroid: Asteroid, trans: Transaction)
    fun interact(missile: Missile, trans: Transaction)
    fun interact(saucer: Saucer, trans: Transaction)
    fun interact(ship: Ship, trans: Transaction)
    fun interactWith(other: Collider, trans: Transaction)
}

The implementations of these are quite similar in the four implementors of the interface, particularly Asteroid and Ship:

class Asteroid
    override fun interact(asteroid: Asteroid, trans: Transaction) {}
    override fun interact(missile: Missile, trans: Transaction) 
    	= checkCollision(missile, trans)
    override fun interact(saucer: Saucer, trans: Transaction) 
    	= checkCollision(saucer, trans)
    override fun interact(ship: Ship, trans: Transaction) 
    	= checkCollision(ship, trans)

    override fun interactWith(other: Collider, trans: Transaction) = other.interact(this, trans)

    private fun checkCollision(other: Collider, trans: Transaction) {
        Collision(this).executeOnHit(other) {
            dieDueToCollision(trans)
        }
    }

class Ship
    override fun interact(asteroid: Asteroid, trans: Transaction) 
    	= checkCollision(asteroid, trans)
    override fun interact(missile: Missile, trans: Transaction) 
    	= checkCollision(missile, trans)
    override fun interact(saucer: Saucer, trans: Transaction) 
    	= checkCollision(saucer, trans)
    override fun interact(ship: Ship, trans: Transaction) { }

    override fun interactWith(other: Collider, trans: Transaction) = other.interact(this, trans)

So, since I have such a fetish about removing duplication, I was thinking that perhaps I could us Kotlin’s by delegation to remove those functions from those two classes. As I think about it now, however, I have doubts. Here is a quote from a programming slack channel, where I expressed my concern.

Here is a more basic question. I was wanting to use delegation by to default the interact methods, with the implementations in the delegate being the most commonly duplicated ones. BUT … those duplicated methods all refer to this at some point. And the by guy has his own this and AFAICT there is no way to give him a pointer to it. (Clearly I could use it to default them all to nothing and then override only the ones I want to see, but that doesn’t save me much, since everyone wants 3 out of 4 or them now.

What I could maybe do is add another function to the interface, e.g. getThis(), and then override only that in the delegators1. I predict that for that to work, you’ll lose the type of this, because it’ll have to be named by its interface name, e.g. SpaceObject instead of Asteroid.

Or, I could just require some kind of okDoIt method in the interface to manage the details. But then I don’t get to save much at all.

Or, and this just popped back into my mind, I could almost do the “close enough” check before calling interact (interactWith?) and pass that result a part of the call. (I have to call anyway, as things stand, because some interactions are accumulating additional information. )

Maybe that’s the bug …

I share that, not because it’s useful, though parts of it may be, but as a demonstration that perfect ideas don’t just pop into my head and into the article in some well-formed condition. I think about things, and often struggle to see quite how something should be. Maybe there are perfect people who just know all things. I am not one of them and have never met one, although I have met a couple who come close.

Code

Kent Beck used to say “let the code participate in our design sessions”, and that’s what we’ll do here. I’m on green code, fully committed right now, so I can experiment without much danger. I think I’ll start by making a class to delegate to and using it in Asteroid.

Ah. As I feared, not gonna work easily. Here’s what I’ve got so far:

class BasicCollider() : Collider {
    override var position: Point = Point.ZERO
    override var killRadius: Double = 0.0

    override fun interact(asteroid: Asteroid, trans: Transaction) {
    }

    override fun interact(missile: Missile, trans: Transaction) {
        checkCollision(missile, trans)
    }

    override fun interact(saucer: Saucer, trans: Transaction) {
        checkCollision(saucer, trans)
    }

    override fun interact(ship: Ship, trans: Transaction) {
        checkCollision(ship, trans)
    }

    override fun interactWith(other: Collider, trans: Transaction) {
        TODO("Not yet implemented")
    }

    private fun checkCollision(other: Collider, trans: Transaction) {
        Collision(this).executeOnHit(other) {
            dieDueToCollision(trans)
        }
    }

    fun dieDueToCollision(trans: Transaction) {
        trans.remove(this)
        trans.add(Splat(this))
        splitIfPossible(trans)
    }

Here are the issues that I see, which are nearly fatal (though perhaps not quite):

  1. Our BasicCollider can’t own the position or killRadius. The position is clearly needed in order to move the asteroid, and the killRadius, I think, will be needed in other calls using Collision.
  2. The call to create Collision refers to (this), which is the BasicCollider itself. This might work if we can get around the issues with position and killRadius.
  3. dieDuetoColliision won’t work here, because this is the BasicCollider, not the asteroid, and we can’t remove it, nor create a Spat on it, nor split it.

I see one possibility. If we were to create the BasicCollider as a member variable of Asteroid (and its other users), we could register with it in init, passing a pointer to the user (the Asteroid in this case), which BasicCollider could then use in its references.

That’s too tricky here. I’ll go back to my simple example and see if I can work out how to do those things with it.

Roll back.

After only a bit of messing about, I get this:

interface DoSomething {
    fun doingIt(input: Int): Int
    fun doingOtherThing(input: Int) :Int
    fun register(client: DoerClient)
}

interface DoerClient {
    fun provideNumber(): Int
}

class BasicDoer(): DoSomething {
    private var client: DoerClient? = null
    override fun doingIt(input: Int): Int = input + (client?.provideNumber() ?: 0)
    override fun doingOtherThing(input: Int): Int = input + 7
    override fun register(client: DoerClient) { this.client = client }
}

class OneDoer(doer: BasicDoer = BasicDoer()): DoerClient, DoSomething by doer {
    init { register(this) }
    
    override fun provideNumber(): Int {
        return 600
    }
}

We’ve added a new tiny interface for our DoSomething guy to use to get information from his client. In this case, we just get a number, but we could now send any message that we define in the DoerClient interface.

In the Doer, we create a BasicDoer and save it in a constructor parameter. (In a real situation, this might be a final defaulted parameter after the list of real parameters.) Then in init, we register, which gives our delegate a pointer back to us, to be used as needed.

Assessment

At this point, I’m about 87% sure that I can, in principle, create a delegate class that will let me remove those five interact methods from Asteroid and Ship, and probably two or three other methods, such as checkCollision and dieOnCollision. We’d have to add register, and deal with the possibility that someone would forget to register. It might still be worth doing, but not because it’s a good idea.

Good for learning.

It might be worth doing so as to gain experience with using the Kotlin by delegation, which is a useful and powerful capability, one that we’d surely benefit from someday.

Not so good for doing.

But today is not that day. By the time we have this in place in Asteroid and Ship, we’ll have had to add an interface, add a secret member variable to Asteroid and Ship, and to add some kind of register initialization. We’ll still have one interact to override, because there’s always one that is empty, same against same, and we’ll want the operational version in the delegate.

Before I built up a better understanding of the by delegate feature, I was thinking that we could just move over a few methods and all would be well. As it turns out, we have to move more than a few, with the additional cost of registration, handling nulls in the delegate, more and larger interfaces, and quite possibly things that I still haven’t discovered.

We might remove some rather simple duplication, but at the cost of increased complexity in the object we’re trying to simplify, plus at least one new interface and additional complication in the delegate class over and above what we already have in the individual classes.

I don’t see how to make it pay off for us.

However …

I’m glad to have learned what I have, and I particularly like the register notion, which seems to me to be potentially useful in setting up a more cooperative kind of helper object, one that has a two-way relationship with the object it’s helping, or that needs some additional information to do its job.

In the case of our collider, for example, we could use the registration to pass in values for position and killRadius even if we didn’t want to give the collider access to the whole object.

Other Possibilities

One useful aspect to trying something that doesn’t work is that we often get ideas about what just might work. I have one more right now.

Recall that all the interaction stuff starts with a call to interactWith, which we currently always implement as other.interactWith(this). Suppose we were to build specific objects that embodied all the interaction logic for a given class, Asteroid, Missile, whoever, and had every object provide one. We might then say: other.interactionLogic().interact(this). Then the other would provide whatever object it wants to have handle its collisions, in whatever form it wants to do it.

It could even return itself, couldn’t it? This would mean that we could move over to this scheme incrementally.

Would that mean that the Collider interface would reduce down to just requiring interactWith? It might.

Hmm. Very interesting. We might explore that idea … but not this morning.

Summary

We’ve learned a bit about Kotlin’s by delegation, and we’ve learned that the only way I see to use it for interactions seems not to bear its own increased weight. But we have had a somewhat deeper think about how it all works, and we have a new notion for what might be an interesting way to break collision logic away from the individual space object classes.

It might lead to a better design, or it might just lead to more learning of ways not to do things. Since I’m here to learn, and if you’re here, you’re either here to learn or to laugh at me, it’ll be worth exploring.

See you next time!



  1. My spell checker thinks this is not a word but I think it is. Surely you recall the famous line “See you later, delegator”.2 

  2. How do you tell the difference between a crocodile and an alligator? The one you see after a while, the other you see later.