Header Ads

O que é "reflexão" na programação?

Shutterstock / whiteMocca

A programação reflexiva é um mecanismo que permite a um processo recursos introspectivos. As APIs de reflexão incorporadas às linguagens de programação permitem que você inspecione o código em tempo de execução. Você pode usar essa capacidade para aprender sobre a base de código circundante e seu conteúdo.

Reflexão é frequentemente falada no contexto da programação orientada a objetos. Você costuma usar reflexão para descobrir entidades de base de código em tempo de execução. A API de reflexão da linguagem permitirá que você inspecione classes, métodos, propriedades e tipos de dentro do seu sistema. Isso permite que você crie uma funcionalidade mais dinâmica.

Sistemas que utilizam reflexão são capazes de interrogar e modificar seus próprios ambientes. É aqui que a reflexão difere da introspecção de valor simples. Uma linguagem com suporte total para reflexão permitirá a modificação da base de código em tempo de execução, permitindo efetivamente que a fonte reescreva aspectos de si mesma.

Um exemplo de reflexão

Um uso comum para reflexão é durante o teste. A reflexão pode ajudá-lo a simular classes, expondo seus comportamentos internos. Um método de classe que é protegido ou privado geralmente não pode ser testado; usando reflexão, você pode substituir a restrição de visibilidade para que se torne pública em seus testes de unidade.

classe Teste & # 123;   protegido int $ Value;   publicfunction __construct & # 40; int $ Value & # 41; & # 123; $ this- > Value = $ Value; & # 125;   função protegida computeValue & # 40; & # 41 ;: int & # 123; return & # 40; $ this- > Value * 2 & # 41 ;; & # 125;   & # 125;   / ** * Sem reflexão * /   $ t = novo teste & # 40; 10 & # 41 ;;   // Erro - o método não é publicamente acessível assert & # 40; $ t- > computeValue & # 40; & # 41; === 20 & # 41 ;;   / ** * Usando o Reflection * /   $ reflectionMethod = new ReflectionMethod & # 40; Test :: CLASS, " computeValue " & # 41 ;; $ reflectionMethod- > setAccessible & # 40; true & # 41 ;;   $ t = novo teste & # 40; 10 & # 41 ;;   // Isso agora funciona! Assert & # 40; $ reflectionMethod- > invoke & # 40; $ t & # 41; === 20 & # 41 ;;

Neste exemplo usando PHP, a classe Test define um método protegido que é usado internamente. Como o método está realizando um cálculo, você pode querer fazer um teste de unidade. Você não pode chamar o método externamente, mas a API Reflection do PHP permite que você ignore as restrições de visibilidade. Uma instância de ReflectionMethod fornece informações sobre o método e permite que você invoque uma versão modificada.

Embora seja útil, você deve estar ciente de que pode ser mal utilizado. O uso generalizado de reflexão em testes geralmente indica problemas maiores em sua base de código. Isso implica a classe &’ interface é excessivamente restritiva e inadequada para suas responsabilidades. Em muitos casos, é mais apropriado refatorar o método protegido em uma nova classe que expõe sua própria interface pública.

Esta é a aparência do exemplo mostrado acima:

classe Calculadora & # 123;   publicfunction computeValue & # 40; & # 41 ;: int & # 123; return & # 40; $ this- > Value * 2 & # 41 ;; & # 125;   & # 125;   classe Teste & # 123;   protegido int $ Value;   Calculadora protegida $ Calculadora;   publicfunction __construct & # 40; int $ Value, Calculator $ Calculator & # 41; & # 123; $ this- > Value = $ Value; $ this- > Calculator = $ Calculator; & # 125;   & # 125;

O componente calculadora agora é sua própria unidade autônoma com uma interface pública testável. Isso anda de mãos dadas com a injeção de dependência – a classe de teste agora recebe uma calculadora que implementa a lógica de cálculo.

Usando reflexão com valores imprevisíveis

A reflexão também é útil quando você está escrevendo um código genérico dentro de uma estrutura. Pode ser necessário interagir com tipos fornecidos pelo usuário que você não pode prever. A reflexão pode ajudar quando você não sabe quais métodos e propriedades um tipo expõe.

Você pode obter uma imagem da funcionalidade do tipo sem qualquer conhecimento prévio de sua fonte. Isso é útil no contexto de componentes de registro e relatório de erros que podem querer descartar a lista de membros de qualquer classe que eles passaram.

Os sistemas de marshalling de dados são frequentemente implementados desta forma também. Imagine um marshaller que pega uma classe e a converte em uma representação JSON. Você pode definir uma convenção de que qualquer método prefixado com get e terminar com Json (por exemplo, getUserJson ()) deve ser chamado pelo empacotador e adicionado à sua saída. A reflexão fornece o mecanismo para obter a lista de métodos. Em seguida, você implementaria a lógica para identificar aqueles que você deveria chamar.

Reflexão, compilação e montagens

O Reflection fornece recursos adicionais em linguagens compiladas que utilizam bibliotecas e assemblies vinculados. As APIs de reflexão permitem que você inspecione o conteúdo dos assemblies carregados. Em linguagens como C #, você pode carregar assemblies adicionais dinamicamente usando Reflection APIs.

Esta abordagem pode ser útil se você estiver implementando um sistema de plug-ins com assemblies fornecidos pelo usuário. Seu programa não saberá quais plug-ins estão disponíveis quando for compilado. Cada vez que for iniciado, ele &’ precisará verificar o sistema de arquivos para encontrar assemblies de plug-in disponíveis. Assim que um plugin é encontrado, o Reflection fornece um mecanismo para carregar e instanciar seus membros.

Você pode inspecionar o plugin, encontrar as classes que ele fornece e registrá-lo com seu aplicativo. Uma inspeção posterior da montagem pode fornecer o nome e a versão do plug-in para exibição em seus registros e na interface do usuário.

A reflexão também pode ser usada para alternar assemblies com base na configuração externa. Digamos que você esteja escrevendo um aplicativo que salva arquivos de imagem no armazenamento. Você pode ter um LocalStorageDriver, FtpStorageDriver e AmazonS3StorageDriver, cada um contido em seu próprio assembly (a . dll em C #).

Usando reflexão, você pode oferecer um “ driver de armazenamento ” chave no arquivo de configuração do seu sistema. O assembly apropriado seria carregado dinamicamente com base no valor do arquivo de configuração ’. Você inspecionaria o assembly para descobrir a classe que implementa sua interface StorageDriver.

Esta abordagem permite que você troque os componentes do seu sistema em tempo de execução. Você não precisa recompilar ou reiniciar seu programa para alternar entre os assemblies. Isso oferece mais flexibilidade e auxilia na implementação das diretivas de configuração.

Declarações de avaliação

A reflexão está intimamente relacionada com a avaliação. Muitas linguagens de programação fornecem uma maneira de executar valores de string dinâmicos como código-fonte.

Eval é uma permutação de reflexão com poder quase ilimitado. Ele permite que você crie e execute um novo código dentro de um programa ao vivo. Isso representa um problema de segurança potencialmente catastrófico se a entrada do usuário for inserida na string de avaliação.

Uma instrução eval deve ser usada quando você ficar sem outras opções. Você precisa ter certeza de que a instrução é executada em um contexto restrito que não pode ser explorado pelos usuários. Lembre-se de que uma injeção de código bem-sucedida daria a um invasor os mesmos poderes que seu código de aplicativo normal.

Conclusão

A reflexão é uma técnica de programação que fornece habilidades introspectivas ao código. O uso eficaz de reflexão permite escrever sistemas mais dinâmicos e se beneficiar de maior automação. Você também pode usar a reflexão para testar a unidade de código privado de outra forma inacessível.

Você precisa ter cuidado. As APIs de reflexão em linguagens de programação são poderosas, portanto, com elas vem a responsabilidade. O maior problema é a capacidade do reflexo de subverter as proteções fornecidas por sua linguagem de programação.

A reflexão permite a existência de cenários que de outra forma seriam impossíveis, como gravações em “ imutável ” variáveis ​​e uso público generalizado de métodos privados. Você deve ser capaz de confiar em seu código para respeitar as regras de seu idioma. O uso das APIs de reflexão deve, portanto, ser considerado cuidadosamente e ter como escopo seções específicas do seu sistema.

Nenhum comentário