Métodos privados e protegidos não deveriam ser acessados fora do escopo permitido de classe ou subclasses. Porém existem algumas situações onde isto é necessário, para mim em alguns contextos de testes automatizados (unitários ou de integração), ou em outros onde estou implementando uma funcionalidade e quero realizar execuções pontuais para verificar comportamentos.
Independente do por quê, e do direcional de boa prática no qual eu afirmaria, não chame métodos privados ou protegidos fora de seus escopos, várias linguagens fornecem suporte a esta possibilidade.
Em PHP e Java, por exemplo, podemos lançar mão de Reflections
(rotinas que permitem explorar classes estruturalmente).
Exemplo de acesso à métodos protegidos em PHP
Vamos verificar como executar um método protegido com visibilidade private ou protected na linguagem PHP (que pode ser usada em frameworks como Laravel e Symphony).
Nossa classe de exemplo é:
class Employee
{
private $salary = 1000;
private function calcBonus()
{
return $this->salary * 0.1;
}
}
Uma classe Employee
com um método privado capaz de calcular o valor do bônus do funcionário.
Usando Reflection com PHP
Com Reflection vamos criar uma classe reflexo a partir da classe Employee
para extrair o método privado calcBonus
e executá-la na sequência:
$reflectionClass = new ReflectionClass(Employee::class);
$reflectionMethod = $reflectionClass->getMethod('calcBonus');
$reflectionMethod->setAccessible(true);
$reflectionMethod->invoke(new Employee()); // '100'
Esta estratégia funcionará com métodos private ou protected.
Podemos refatorar este código e acessar com reflection o método desejado sem precisar instanciar uma reflection da classe completa:
$reflectionMethod = new ReflectionMethod(Employee::class, 'calcBonus');
$reflectionMethod->setAccessible(true);
$reflectionMethod->invoke(new Employee()); // '100'
Caso precise passar argumentos para o método, ao invés de invoke
utilize ReflectionMethod::invokeArgs
.
Vamos refatorar nossa classe para que o método calcBonus
receba o fator de cálculo:
class Employee
{
private $salary = 1000;
private function calcBonus($factor)
{
return $this->salary * $factor;
}
}
Então podemos executar o método definindo os argumentos passando-os como um array, neste caso mesmo passando um único argumento deveremos enviar dentro do array:
$args = [0.1];
$reflectionMethod->invokeArgs(new Employee(), $args);
Função Helper para executar métodos protegidos
Por fim, adicione esta função com um helper
em sua caixa de ferramentas, facilitando a execução de métodos protegidos e privados em PHP no futuro:
/**
* Calls object method with arguments.
*
* @param object $object
* @param string $method
* @param array $args
* @return mixed
*/
function callProtectedMethod(object $object, string $method, array $args = [])
{
$reflectionMethod = new ReflectionMethod(get_class($object), $method);
$reflectionMethod->setAccessible(true);
return $reflectionMethod->invokeArgs($object, $args);
}
Execute o helper como no exemplo a seguir:
callProtectedMethod(new Employee(), 'calcBonus'); // without arguments
//or
callProtectedMethod(new Employee(), 'calcBonus', [0.2]); // with arguments
Considerações finais
Se você está testando criar testes para validar métodos privados em classes, é provável que tenha cometido algum engano na arquitetura deste código. Ainda assim, esta possibilidade não é descartada em uma estratégia que uso no dia a dia: vou construindo métodos privados sendo executados por chamadas diretas para validar minha lógica em complexidades ainda não dominadas da regra de negócio. Ao fazer isso crio a possibilidade de acelerar o desenvolvimento, e posteriormente retirar este código teste do meu codebase final.
Pense na estratégia de uso e aplique sempre que o benifício for maior que o conceito puro da técnica.
Comentários