Home > Software design >  Best way to hide internal classes by interfaces
Best way to hide internal classes by interfaces

Time:08-09

what is the best way to hide an impl class behind an interface?

The companion creator works well, but needs to repeat all the arguments of the constructor twice. Is there a way to simplify A.from(x,y,z,a,b,c,d) = AImpl(x,y,z,...)?

//the application will only see this interface
interface A {
  val x: X
  val y: Y

  companion object {
     //choose any implementation without breaking existing code
     @JvmStatic
     fun from(x: X, y: Y): A = AImpl(x = x, y = y) //inconvenient for many arguments
  }
}

internal class AImpl(
   override val x: X,
   override val y: Y
) : A

fun someApplication() {
   val a = A.from(..., ...) //no reference to AImpl
   //use a: A
}

Thank you very much.

Martin

CodePudding user response:

I don't really understand what are you tring to do. So i'm not sure if this will be the answer to your question.

fun main() {

        val x = X()
        val y = Y()
        val a = AImpl().from(x,y)
        
        a.x.toString()
        a.y.toString()
        
        //use object class so you can use the class without ()
        val b = BImpl.from(x,y)
        
        b.x.toString()
        b.y.toString()
        
}

class X{}
class Y{}


interface A
{
    val x : X
    val y : Y
    
    fun from(xValue : X, yValue : Y) : A
}

internal class AImpl() : A {
    override var x : X = X()
    override var y : Y = Y()
    
    override fun from(xValue : X, yValue : Y) : A {
        x = xValue
        y = yValue
        
        return this
    }
    
}


object BImpl : A {
    override var x : X = X()
    override var y : Y = Y()
    
    override fun from(xValue : X, yValue : Y) : A {
        x = xValue
        y = yValue
   
        return this
    }
}

I don't think you should implementation anything in Interface but If you want your code to work

  companion object {
     //choose any implementation without breaking existing code
     @JvmStatic
     //fun from(x: X, y: Y): A = AImpl(x = x, y = y) 
     //because you Return A   That's why you got no reference to AImpl
     //so don't return A but make it AImpl directly
     //but it's still act like "A" interface.
     fun from(x: X, y: Y) = AImpl(x,y)
  }
}

CodePudding user response:

The way you wrote it is already good. If you're concerned about too many parameters, note that you don't have to name and pass every single one of them, and not every property needs to be passed as a constructor parameter. Depending on the implementation class, some properties can be defaulted or calculated:

interface A {
    val x: X
    val y: Y

    companion object {
        fun from(x: X, y: Y): A = AImpl(x)
    }
}

internal class AImpl(
    override val x: X,
) : A {
    override val y = something()
}

Furthermore, AImpl doesn't have to be internal; it can be private or even an anonymous class, if it's a very short one (otherwise the code can become unreadable):

interface A {
    val x: X
    val y: Y

    companion object {
        fun from(x: X, y: Y): A = object : A {
            override val x = x
            override val y = y
        }
    }
}

Instead of using a member function of a companion object, a common Kotlin-idiomatic way is to use a standalone factory function with the same name as the interface:

fun A(x: X, y: Y): A = AImpl(x, y)

An example of such a function is MutableList: it has the same name as interface MutableList, and it happens to return an ArrayList but that's just implementation detail: it's only documented to return something that implements interface MutableList.

  • Related