Pool de objetos genéricos com CoffeeScript

Aqui está outro uso do meu auxiliar de mixins anterior para criar facilmente pools de objetos.

Conjuntos de objetos permitem reutilizar instâncias de uma classe para evitar sobrecarga de coleta de lixo ao liberar essas instâncias. As instâncias são criadas apenas se nenhuma outra instância estiver disponível e devem ser explicitamente liberadas por meio do pool.

Vamos começar com um rápido lembrete, as extensões da função:

Function::include = (mixins...) ->
excluded
= ['constructor']
for mixin in mixins
excl
= excluded.concat()
excl
= excl.concat mixin::excluded if mixin::excluded?
@::[k] = v for k,v of mixin.prototype when k not in excl
mixin
.included? this

this

Function::extend = (mixins...) ->
excluded
= ['extended', 'included']
for mixin in mixins
excl
= excluded.concat()
excl
= excl.concat mixin.excluded if mixin.excluded?
@[k] = v for k,v of mixin when k not in excl
mixin
.extended? this

this

Function::concern = (mixins...) ->
@include.apply(this, mixins)
@extend.apply(this, mixins)

Isso é um pouco mais complexo do que na minha dica anterior. Abaixo uma rápida explicação dessas extensões.

  • O método decora o protótipo da classe com o protótipo do mixin aprovado.Function::include
  • O método decora a classe com as propriedades do objeto passado.Function::extend
  • O decora ambos.Function::concern

As propriedades podem ser excluídas usando uma excludedpropriedade definida no protótipo ou na classe de acordo com o tipo de injeção usada.

Ganchos na inclusão / extensão podem ser criados usando as propriedades includedou extendedna classe mixin.

The Instances Pool Mixin

Agora vamos criar o InstancesPoolmixin.

O InstancesPoolfornecerá dois métodos de classe para recuperar / liberar instâncias e armazenará essas instâncias em dois arrays. Um para instâncias usadas, o outro para instâncias liberadas.

Uma restrição nas classes agrupáveis ​​é que elas devem evitar ter argumentos de construtor. Como a instância será reutilizada, o construtor será chamado apenas uma vez. Para contornar que, em vez do construtor, as classes agrupáveis ​​devem fazer sua inicialização no initmétodo. Este método será chamado sempre que a instância for movida do pool não utilizado para o pool usado.

class InstancesPool
# The two objects stores are created in the extended hook to avoid
# that all the class extending `InstancesPool` shares the same instances.
@extended: (klass) ->
klass
.usedInstances = []
klass
.unusedInstances = []

# The `get` method returns an instance of the class.
@get: (options={}) ->
# Either retrieve or create the instance.
if @unusedInstances.length > 0
instance
= @unusedInstances.shift()
else
instance
= new this

# Stores the instance in the used pool.
@usedInstances.push instance

# Init the instance and return it.
instance
.init(options)
instance


# The `release` method takes an instance and move it from the
# the used pool to the unused pool.
@release: (instance) ->
# We can't release unused instances of instances created using
# the `new` operator without using `get`.
unless instance in @usedInstances
throw new Error "Can't release an unused instance"

# The instance is removed from the used instances pool.
index
= @usedInstances.indexOf(instance)
@usedInstances.splice(index, 1)

# And then moved to the unused instances one.
@unusedInstances.push instance

# Default `init` implementation, just copy all the options
# in the instance.
init
: (options={}) -> @[k] = v for k,v of options

# Default `dispose` implementation, call the `release` method
# on the instance constructor. A proper implementation should
# take care of removing/cleaning all the instance properties.
dispose
: -> @constructor.release(this)

Agora vamos criar uma classe agrupável e tentar criar / liberar algumas instâncias:

class PoolableClass
@concern InstancesPool

instance1
= PoolableClass.get(x: 10, y: 20)
instance2
= PoolableClass.get(x: 20, y: 10)

console
.log 'used:', PoolableClass.usedInstances.length
console
.log 'unused:', PoolableClass.unusedInstances.length

instance2
.dispose()

console
.log '----'
console
.log 'used:', PoolableClass.usedInstances.length
console
.log 'unused:', PoolableClass.unusedInstances.length

instance3
= PoolableClass.get()

console
.log '----'
console
.log 'used:', PoolableClass.usedInstances.length
console
.log 'unused:', PoolableClass.unusedInstances.length

instance1
.dispose()
instance3
.dispose()

console
.log '----'
console
.log 'used:', PoolableClass.usedInstances.length
console
.log 'unused:', PoolableClass.unusedInstances.length