Estender única classe abstrata
-
10-07-2019 - |
Pergunta
Se você tivesse uma classe de fábrica que cria novos objetos de algum tipo, e que classe factroy é um singleton, como este:
class Database_Factory extends Base_Factory {
private static $factory;
private $objects = array();
public function __get($profile) {
// check for object and return it if it's created before
}
public static function getInstance(){
if (!self::$factory)
self::$factory = new self();
return self::$factory;
}
}
O mesmo se repete código qualquer momento onde algum objeto precisa de sua própria fábrica. Então eu decidi fazer esta classe abstrata fábrica e implementar rotinas única específicas para cada fábrica. Mas PHP não permite instanciar a classe abstrata.
abstract class Base_Factory {
public static function getInstance(){
if (!self::$factory)
self::$factory = new self();
return self::$factory;
}
}
Erro fatal: Não é possível instanciar abstract class Base_Factory
O que você faria?
Solução
métodos PHP, self
refere-se sempre a classe em que o método é definido. Desde a versão 5.3.0, o PHP suporta “ligação estática tardia”, onde você pode usar a palavra-chave static
ao acesso substituído métodos estáticos, bem como a get_called_class()
função para obter o nome da classe derivada no contexto estático.
No entanto, seu projeto tem uma grande falha: O $factory
propriedade estática definida no Base_Factory
é compartilhado entre todas as classes derivadas. Portanto, a primeira vez que um singleton é criado e armazenado nesta propriedade, todas as outras chamadas para getInstance()
irá retornar o mesmo objeto, não importa o derivado classe é usada.
Você pode usar um nomes de classe mapeamento dicionário estáticos para objetos únicos:
abstract class Base_Factory {
private static $_instances = array();
public static function getInstance() {
$class = get_called_class();
if (!isset(self::$_instances[$class])) {
self::$_instances[$class] = new $class();
}
return self::$_instances[$class];
}
}
Ah, mais uma coisa: o fato de que você está procurando uma possibilidade de reutilização de código para objetos únicos poderia ser uma sugestão para o fato de que você está sobre-usando o padrão de design singleton! Pergunte-se se as classes que você está planejando implementar como singletons são realmente únicos e se não haverá nenhum caso de uso onde você pode querer ter múltiplas instâncias da classe particular.
Muitas vezes, é muito melhor usar apenas um Singleton que representa a corrente “contexto de aplicação” que fornece acessores para objetos que são singletons no que diz respeito a este contexto.
Outras dicas
PHP 5.3 +
abstract class Singleton
{
/**
* Instance
*
* @var Singleton
*/
protected static $_instance;
/**
* Constructor
*
* @return void
*/
protected function __construct() {}
/**
* Get instance
*
* @return Singleton
*/
public final static function getInstance() {
if (null === static::$_instance) {
static::$_instance = new static();
}
return static::$_instance;
}
}
PHP> = 5,3 única
abstract class Base_Factory {
protected static $factory;
public static function getInstance(){
if (!self::$factory) {
$class = get_called_class();
self::$factory = new $class();
}
return self::$factory;
}
}
Registre seus singletons em uma classe simples como este
class Singletons {
static private $singleton = array();
public function getSingleton($class) {
if (!isset(self::$singleton[$class])) {
self::$singleton[$class] = new $class;
}
return self::$singleton[$class];
}
}
Em seguida, fazer isso
class aSingleton {
public $i;
public function test() {
++$this->i;
echo get_class() . " called {$this->i} times\n";
}
}
Singletons::getSingleton('aSingleton')->test();
Singletons::getSingleton('aSingleton')->test();
saída
aSingleton called 1 times
aSingleton called 2 times
Por definição classess abstrata não pode ser instanciado em PHP como qualquer outras linguagens orientadas a objeto. Portanto, o seu Base_Factory deve ser interface em vez de classe abstrata.
A partir do manual do PHP: "Não é permitido para criar uma instância de uma classe que tem sido definido como abstrato"
Bem, você poderia fazer uma verificação para garantir que a classe chamando a função não é a Base_Factory.
if(__CLASS__!='Base_Factory')
Em seguida, use $ this em vez de auto para se referir ao objeto atual em vez da classe atual.
if (!$this->factory)
$this->factory = new self();
return $this->factory;