Reference Counting Caching Factory
This class defines a special type of cache that either return or constructs a new instance of objects of type V, given.
Description
The gist of this cache is that it will create a new instance of an object only if there is not already a satisfying instance in the it, hence avoiding duplicates. A couple of requirements have to be met :
the "Value Type" V (the type of objects that are sored in the cache) must implement the ReferenceCounted interface,
the "Value Type" must provide a 1-argument constructor, of type K, a.k.a the "Key Type",
the cache is using a hash table internally to memorize the active instances. The keys of this map are instances of the "Key Type" K, so be careful of the correctness of the hashCode and equals for K. Kotlin data classes will work out of the box.
Usage
Define a "Key type":
data class ConnectionInfo(val host: String, val port: Int)
Then define a "Value type", that implements the ReferenceCounted interface, and with a 1-arg of the former type constructor. For example if you'd like to implement a database connection pool, you can wrap it in such a class, like in the example below:
class PooledDatabaseConnection(info: ConnectionInfo): ReferenceCounted {
init { ... }
companion object {
private val pool = ReferenceCountingCachingFactory(ConnectionInfo::class.java, PooledDatabaseConnection::class.java)
fun get(val host: String, val port: Int): PooledDatabaseConnection {
return pool.get(ConnectionInfo(host, port))
}
}
var connected = false
private fun connect() { ... }
private fun disconnect() { ... }
fun onReferenceReleased(remainingRefs: Int) {
if (remainingRefs == 0 && connected) { disconnect() }
}
fun onReferenceAcquired(currentRefCount: Int) {
if (!connected) { connect() }
}
fun close() { pool.release(this) }
}
To obtain a connection, you would use PooledDatabaseConnection.get("host.example", 1234)
. If such a connection is already existing, it will be reused, otherwise it will be created. To give a chance to the garbage collector to dispose of useless objects, you should release it from the pool (e.g. the close()
function above), it will decrease the internal reference counter by one. Once the reference count reaches 0, the object will be removed from the cache.
Note
As type parameters cannot be reified, their runtime class must be passed in the constructor. The type inferer can infer the type parameters from the constructor parameters, so a new instance can be created without code redundancy:
val myCache = ReferenceCountingCachingFactory(MyKeyType::class.java, MyCachedType::class.java)