Em uma classe PHP5, quando um construtor privado é chamado?
-
09-06-2019 - |
Pergunta
Digamos que eu esteja escrevendo uma classe PHP (>= 5.0) que pretende ser um singleton.Todos os documentos que li dizem para tornar o construtor da classe privado para que a classe não possa ser instanciada diretamente.
Então, se eu tiver algo assim:
class SillyDB
{
private function __construct()
{
}
public static function getConnection()
{
}
}
Há algum caso em que __construct() é chamado diferente de se eu estiver fazendo um
new SillyDB()
chamar dentro da própria classe?
E por que posso instanciar o SillyDB de dentro dele?
Solução
__construct()
só seria chamado se você o chamasse de dentro de um método para a classe que contém o construtor privado.Então, para o seu Singleton, você pode ter um método como este:
class DBConnection
{
private static $Connection = null;
public static function getConnection()
{
if(!isset(self::$Connection))
{
self::$Connection = new DBConnection();
}
return self::$Connection;
}
private function __construct()
{
}
}
$dbConnection = DBConnection::getConnection();
A razão pela qual você pode/gostaria de instanciar a classe de dentro dela é para que você possa verificar se existe apenas uma instância em um determinado momento.Afinal, esse é o objetivo de um Singleton.Usar um Singleton para uma conexão de banco de dados garante que seu aplicativo não faça muitas conexões de banco de dados ao mesmo tempo.
Editar: Adicionado $, conforme sugerido por @emanuele-del-grande
Outras dicas
Aqui está um singleton muito simples que apenas gera uma string de data/hora:
class TheDate
{
private static $DateInstance = null;
private $dateVal;
public static function getDateInstance()
{
if(!isset(self::$DateInstance))
{
self::$DateInstance = new TheDate();
}
return self::$DateInstance;
}
public static function getDateVal()
{
return self::$DateInstance->dateVal;
}
private function __construct()
{
$this->dateVal = strftime("%Y-%m-%d %H:%M:%S");
}
}
Fazer algo assim obviamente me dá a mesma data repetidamente:
$date1 = TheDate::getDateInstance();
echo $date1->getDateVal() . "<br />";
$date2 = TheDate::getDateInstance();
echo $date2->getDateVal() . "<br />";
E fazer isso não gera nenhum erro:
class NewDate extends TheDate
{
public function __construct()
{
}
}
OK, fiz alguns testes por conta própria.
- Se você não declarar seu próprio construtor na subclasse, tentar instanciá-lo gerará um erro fatal, pois ele tenta chamar o construtor da superclasse.
- No caso de uma conexão de banco de dados, quando você provavelmente está retornando (em PHP, pelo menos) uma instância mysqli ou o recurso retornado pelo
mysql_connect()
função (ou algum outro link para algum outro RDBMS), contanto que você marque a instância como privada, não há ameaça de alguém subclassificá-la e adulterar o link. - Como mencionei antes, se alguém realmente quiser substituir seu comportamento e fazer múltiplas conexões, poderia muito bem fazê-lo escrevendo uma nova classe.
Teste o código, Mark, e me conte o que você descobriu.
EDITAR:Além disso, nesta situação específica, não sei por que você precisaria se preocupar em impedir que uma pessoa subclasse.Se alguém tiver acesso ao seu código PHP para subclassificá-lo, também terá acesso ao seu código para copiá-lo e alterar os modificadores de acesso para algo que (por qualquer motivo) considere adequado.
A utilidade prática do Singleton neste caso é que, ao usá-lo, você pode garantir que estará sempre usando a mesma conexão de banco de dados para uma determinada solicitação HTTP.Isso é feito.As outras coisas (usando final
e construtores privados) é útil saber do ponto de vista teórico, e ainda mais útil saber se você deseja distribuir código com qualidade de API para outros programadores, mas no caso deste exemplo específico, tudo o que as palavras-chave estão fazendo é adicionar bytes a o tamanho do seu arquivo de classe.
Uma pequena escolha:Para completar, você provavelmente declarar a classe como final também, já que você não gostaria que alguém subclassificasse essa classe e implementasse seu próprio construtor público.
(Perdoe-me se o compilador detectar a substituição de um construtor privado, mas acho que não)