mercredi 2 décembre 2020

Interface or Object with Type?

I have a calculation program which uses a number of different vertex types. Currently I have euclidean and geo, and I have decided to create an interface. Now I am wondering if that is a good idea.

interface Vertex: Comparable<Vertex> {
    var id: Int

    /** returns then distance to another vertex. Vertex is expected to be of exact same type */
    fun distanceTo(vertex: Vertex): Double

    override fun compareTo(other: Vertex): Int {
        return id.compareTo(other.id)
    }
}

class Vertex2d(override var id: Int, val x: Double, val y: Double) : Vertex {
    override fun distanceTo(vertex: Vertex): Double {
        vertex as Vertex2d
        val distX: Double = x - vertex.x
        val distY: Double = y - vertex.y
        return (sqrt(distX * distX + distY * distY) * 100000.0).roundToInt() / 100000.0 // hypot takes 3 times as long
    }
}

class VertexGeo(override var id: Int, val latitudeRadians: Double, val longitudeRadians: Double) : Vertex {
    override fun distanceTo(vertex: Vertex): Double {
        vertex as VertexGeo
        // some complex calculation
        return distance
    }
}

The issue is, I always need a cast in distanceTo and this only work if the type is correct. I should actually check additionally isInstanceof, but no need in my progam.

Since the calculation is run billions of times (actually faster than storing the result in a memory table) performance is also an issue, but do not weigh is too much. Clean design is more a priority.

Instead I could do just one Vertex object with a type. Having the same signature I can use factory methods, but that is a minor detail.

enum class VertexType { EUC, GEO }

/**
 * @param x: x in EUC or longitude in radians
 */
class VertexInOne(var id: Int, val x: Double, val y: Double, val type: VertexType): Comparable<VertexInOne> {

    val latitudeRadians
        get() = y
    
    val longitudeRadians
        get() = x
    

    fun toDistance(other: VertexInOne) {
        when (type) {
            VertexType.EUC -> {
                // calc distance
                return distance
            }
            VertexType.GEO -> {
                // calc distance
                return distance
            }
        }
    }

    override fun compareTo(other: VertexInOne): Int {
        return id.compareTo(other.id)
    }

    companion object {
        fun createVertexEuc(id: Int, x: Double, y: Double): VertexInOne {
            return VertexInOne(id, x, y, VertexType.EUC)
        }
        fun createVertexGeo(id: Int, latRadians: Double, longRadians: Double): VertexInOne {
            return VertexInOne(id, longRadians, latRadians, VertexType.GEO)
        }
    }
}

What would be your choice and why?

Aucun commentaire:

Enregistrer un commentaire