DevSecOps

20 jun, 2011

Construtores privados apropriados para ActionScript 3.0

Publicidade

Aqui está uma dica útil para programadores
Flash e Flex. O ActionScript 3.0 é ótimo, mas não tem nenhum Singleton.

Samuel Aglesias comentou, certa vez, sobre uma solução que previne, com sucesso, as
subclasses de chamarem o construtor usando super(), mas ela não previne outros
códigos de criarem novas instâncias de classe com new.

Portanto, eu apresento a você uma maneira
engenhosa de tornar um construtor verdadeiramente privado: adicione um
argumento necessário ao construtor, que deve ser configurado com um valor secreto
que somente é conhecido pela classe:

class Singleton {

private static var _instance:Singleton = null;

// secret known only to this class
private static const secret:Number = Math.random();

/**
* @private
*/
public function Singleton(enforcer:Number) {
if (enforcer != secret) {
throw new Error("Error: use Singleton.instance instead");
}
}

/**
* Global single instance
*/
public static function get instance():Singleton {
if (_instance == null) {
_instance = new Singleton(secret);
}
return _instance;
}
}

Esse método também tem a vantagem de ser bastante rápido, e oferecer um nível de proteção na compilação: new Singleton() irá falhar em tempo de compilação. O usuário faria
a compilação funcionar se passasse, por exemplo, new Singleton(42), mas existe
uma chance muito grande* de que isso falhe na hora da execução.

Use essa
técnica sempre que o construtor deva ser chamado somente de dentro de uma
classe, para que seus usuários não cometam o erro de criar uma segunda
instância do singleton.

* Nerds à
parte: a mudança em adivinhar o resultado do Math.random() é 1 para 2^52, ou
1/4,503,599,627,370,496. Isso acontece porque os floating point numbers de 64 bits têm uma
mantissa de 52 bits.

Update: Grant Skinner tem outra maneira de fazer a mesma coisa. A dele é
provavelmente mais elegante, mas menos divertida, e a diversão é uma
característica que eu valorizo em hacks de linguagens. Além disso, sua solução não é
verificada na hora da compilação. Andrew Trice  tentou também. A versão dele requer um
argumento extra que forneça a verificação na hora da compilação, mas ele pode
ser derrotado pelo chamado new Singleton(Singleton.getInstance). Obrigado
pelos links, Shaun.

Update 2: Eric J Feminella tem uma
solução
que
é indiscutivelmente melhor que a minha, uma vez que ela é propriamente checada
na hora da compilação, p.e., você não consegue enganar passando um inteiro de argumento, adiando o erro para tempo de execução (ao invés de lançar uma exceção, retornar nulo também funcionaria). Mas eu ainda acho que a minha solução é mais divertida…