frameworks

Autowiring em Container de Injeção de Dependência

No artigo Entendendo Injeção de Dependência vimos sobre o que é injeção de dependência, seu funcionamento e como se dá a sua aplicação. No artigo Container De Injeção De Dependência (DI Container) vimos como funciona um container para gerenciar o mapa de dependências.

Nesse artigo veremos uma funcionalidade relativamente comum nos containers de dependência de frameworks que é a habilidade de resolver as dependências de um construtor (principalmente) automaticamente, chamada de autowiring.

No artigo Container De Injeção De Dependência (DI Container) implementamos um protótipo (simples, porém funcional) de um container e, em determinado momento, chegamos nesse tipo de caso de uso:

$indexController = new IndexController(
    $container->get('user.repository')
);

$userController = new UserController(
    $container->get('user.repository')
);

$registerController = new RegisterController(
    $container->get('user.repository')
);

Alguns controladores precisavam receber a instância de UserRepository e tivemos que manualmente passá-la para cada um deles. Agora, imagine se cada um desses controladores tivesse que receber outras dependências? Nenhum problema, só que ficaria meio chato e improdutivo ter que ficar repetindo essas construções na instanciação deles, concorda?

E se desenvolvermos uma forma do nosso container automaticamente resolver essas dependências? Desse modo, tudo o que faríamos seria pedir para o Container uma instância desses controladores, sem a necessidade de nos preocuparmos em passar manualmente para o construtor de cada um deles as suas dependências. Teríamos como resultado um código assim:

$indexController = $container->get(IndexController::class);

$userController = $container->get(UserController::class);

$registerController = $container->get(RegisterController::class);

Estamos pedindo as instâncias desses controladores sem nos preocuparmos em alimentá-los de suas dependências, deixamos esse trabalho para o Container.

Symfony - Fundamentos
Curso de Symfony - Fundamentos
CONHEÇA O CURSO

Estrutura inicial do projeto

Vamos criar o protótipo de uma aplicação para que possamos testar a nossa implementação de Container. A nossa aplicação terá essa estrutura:

- [blog-artigo-di]
- - - [app]
- - - - [Http]
- - - - - - [Controller]
- - - - - - - UserController.php
- - - - [Repositories]
- - - - - LeadRepository.php
- - - - - TagRepository.php
- - - - - UserRepository.php
- - - Container.php
- index.php

O repositório dessa estrutura no GitHub você encontra clicando aqui. Se desejar, você pode fazer download direto dela clicando nesse link: sem-autowiring.zip

Depois de baixar e colocar no local onde normalmente você executa seus projetos, basta que você execute composer install pois o projeto faz uso da PSR-4 para autoloading:

$ ~/D/w/blog-artigo-di> composer install

O nosso Container atualmente possui uma implementação simples e que ainda não suporta a resolução automática de dependências. Abra app/Container.php:

<?php
declare(strict_types=1);

namespace App;

use Closure;

final class Container
{
    private $instances = [];

    public function set(string $id, Closure $closure) : void
    {
        $this->instances[$id] = $closure;
    }

    public function get(string $id) : object
    {
        return $this->instances[$id]($this);
    }

    public function singleton(string $id, Closure $closure) : void
    {
        $this->instances[$id] = function () use ($closure) {
            static $resolvedInstance;

            if (null !== $resolvedInstance) {
                $resolvedInstance = $closure($this);
            }

            return $resolvedInstance;
        };
    }
}

Esse container já foi explicado na indicação de leitura no início desse artigo. Portanto, não vamos entrar novamente em seus pormenores.

O arquivo index.php na raiz do projeto é o responsável pelo bootstrap da nossa aplicação protótipo:

<?php

// Carrega o autoload do Composer
require './vendor/autoload.php';

use App\Repositories\TagRepository;
use App\Repositories\LeadRepository;
use App\Repositories\UserRepository;
use App\Http\Controller\UserController;

// Instancia o container
$container = new App\Container();

// Adiciona referências ao container
$container->set(TagRepository::class, function() {
    return new TagRepository();
});

$container->set(LeadRepository::class, function() {
    return new LeadRepository();
});

$container->set(UserRepository::class, function() {
    return new UserRepository();
});

// Instancia o UserController passando para ele as dependências necessárias
$userController = new UserController(
    $container->get(TagRepository::class),
    $container->get(UserRepository::class),
    $container->get(LeadRepository::class)
);

$userController->index();

Para testar o exemplo, na raiz do projeto execute:

$ ~/D/w/blog-artigo-di> php -S localhost:8000

Isso executará o servidor embutido do PHP em cima da raiz do nosso projeto, permitindo que o acessemos pelo navegador através da URL: http://localhost:8000/

O resultado será:

/Users/kennedytedesco/Documents/www/blog-artigo-di/app/Http/Controller/UserController.php:31:
array (size=2)
  'id' => int 1
  'name' => string 'tag' (length=3)

/Users/kennedytedesco/Documents/www/blog-artigo-di/app/Http/Controller/UserController.php:31:
array (size=2)
  'id' => int 2
  'name' => string 'user' (length=4)

/Users/kennedytedesco/Documents/www/blog-artigo-di/app/Http/Controller/UserController.php:31:
array (size=2)
  'id' => int 3
  'name' => string 'lead' (length=4)

Indica que o método index() do UserController foi executado com sucesso. Ou seja, as dependências, os repositórios necessários para o funcionamento dessa classe foram injetados com sucesso. Até aqui tudo bem, ademais, injetamos tais dependências manualmente:

$userController = new UserController(
    $container->get(TagRepository::class),
    $container->get(UserRepository::class),
    $container->get(LeadRepository::class)
);

Implementando a resolução automática de dependências

Para que possamos implementar a resolução automática das dependências utilizaremos a API de reflexão do PHP. Essa API nos fornece meios para que façamos engenharia reversa nas classes, extraindo muitas de suas informações internas como quantos métodos possui, quais são públicos, protegidos ou privados, se implementa um construtor, quais são os parâmetros do construtor, se são opcionais ou exigidos, entre outras informações.

Dessa forma, se o usuário pedir para o Container a instanciação do UserController:

$userController = $container->get(UserController::class);

Que tal a gente usar reflexão para obter quais dependências (classes) ele necessita para ser instanciado e então resolver essas dependências automaticamente usando o próprio Container e retornar a instância dele?

É exatamente isso que a nossa implementação de autowiring fará. Para tanto, começaremos alterando o código do nosso container app\Container.php para:

<?php
declare(strict_types=1);

namespace App;

use Closure;
use ReflectionClass;
use ReflectionParameter;

final class Container
{
    private $instances = [];

    public function set(string $id, Closure $closure) : void
    {
        $this->instances[$id] = $closure;
    }

    public function get(string $id) : object
    {
        // Se essa referência existe no mapa do container, então a retorna diretamente.
        if ($this->has($id)) {
            return $this->instances[$id]($this);
        }

        // Se a referência não existe no container, então foi passado uma classe para ser instanciada
        // Façamos então a reflexão dela para obter os parâmetros do método construtor
        $reflector = new ReflectionClass($id);
        $constructor = $reflector->getConstructor();

        // Se a classe não implementa um método construtor, então vamos apenas retornar uma instância dela.
        if (null === $constructor) {
            return new $id();
        }

        // Itera sobre os parâmetros do construtor para realizar a resolução das dependências que ele exige.
        // O método "newInstanceArgs()" cria uma nova instância da classe usando os novos argumentos passados.
        // Usamos "array_map()" para iterar os parâmetros atuais, resolvê-los junto ao container e retornar um array das instâncias já resolvidas pelo container.
        return $reflector->newInstanceArgs(array_map(
            function (ReflectionParameter $dependency) {
                // Busca no container a referência da classe desse parâmetro
                return $this->get(
                    $dependency->getClass()->getName()
                );
            },
            $constructor->getParameters()
        ));
    }

    public function singleton(string $id, Closure $closure) : void
    {
        $this->instances[$id] = function () use ($closure) {
            static $resolvedInstance;

            if (null !== $resolvedInstance) {
                $resolvedInstance = $closure($this);
            }

            return $resolvedInstance;
        };
    }

    public function has(string $id) : bool
    {
        return isset($this->instances[$id]);
    }
}

(Através do GitHub você pode visualizar o que foi adicionado/removido. Basta visualizar esse commit aqui).

A partir do momento que o nosso Container consegue resolver as dependências automaticamente, podemos alterar no index.php a forma de instanciar, que era uma instanciação direta da classe usando new UserController:

$userController = new UserController(
    $container->get(TagRepository::class),
    $container->get(UserRepository::class),
    $container->get(LeadRepository::class)
);

Para uma instanciação que usa o Container (para que ele possa resolver as dependências para nós):

$userController = $container->get(UserController::class);
$userController->index();

(O código completo do exemplo com autowiring encontra-se no branch master do repositório desse exemplo).

Você pode testar o exemplo e terá o mesmo resultado obtido anteriormente quando não usávamos autowiring. O UserController continuará sendo instanciado com sucesso.

Laravel - Framework PHP (Parte 3/3)
Curso de Laravel - Framework PHP (Parte 3/3)
CONHEÇA O CURSO

Concluindo

Vimos nesse artigo a fundação sobre como a resolução automática de dependências é feita nos containers de injeção de dependência. É por esse caminho que Symfony, Laravel entre outros frameworks (inclusive de outras linguagens) fazem. Obviamente o nosso Container é simples, didático e bem direto ao ponto, não estando 100% pronto para uso em projetos reais. Algumas verificações de segurança (se a classe existe, senão lançar uma exceção etc) precisariam ser implementadas. Na realidade, existem boas implementações de containers por aí e, se você usa um Web Framework, não vai precisar criar a sua própria. No entanto, saber como funciona, é essencial. Essa foi a intenção desse artigo.

Até a próxima!

Tendências de TI para 2019

O ano já começou a todo vapor! Por isso, nada melhor do que estar por dentro das tendências na área de TI para 2019. Além das coisas mudarem muito rápido, sempre aparece alguma novidade. Então, vamos começar o ano nos atualizando com as novidades e quais tecnologias continuam em alta neste ano que se inicia.

DevOps

Hoje em dia, quem conhece a cultura e as ferramentas DevOps conquista um bom destaque no mercado. Neste ano, a previsão é que este segmento da área de TI fique mais aquecido ainda.

Relembrando: DevOps é um modelo que combina práticas e ferramentas com a intenção de aumentar a capacidade de uma empresa distribuir seus serviços de forma muito mais rápida. Ela visa também a integração da área de desenvolvimento com a área de operação, pois assim conseguimos alcançar uma maior qualidade nas entregas, além de evitar falhas de comunicação entre as áreas, bem como atrasos e retrabalhamos nos projetos.

Essa é uma prática que vem ganhando muito destaque por causa dos processos de integração contínua e entrega contínua. As entregas são menores, visando as liberações de versões mais seguras, além dos ciclos de desenvolvimento menores, fazendo com que a qualidade aumente.

Outro ponto muito importante é o auxílio que ele traz no gerenciamento e controle sobre o ambiente e infraestrutura. Como a infraestrutura é gerenciada através de técnicas de desenvolvimento de software, tanto ela quanto os servidores são implantados muito rapidamente.

Docker DevOps - Para desenvolvedores
Curso de Docker DevOps - Para desenvolvedores
CONHEÇA O CURSO

Mas, como podemos implementar a cultura e as ferramentas DevOps?

Existem diversas ferramentas para nos ajudar. Possivelmente, você já deve ter ouvido falar sobre Docker. O Docker é um exemplo, assim como o Kubernetes, o Openshift, o Jenkins e outras ferramentas. Essas ferramentas facilitam a criação e manutenção desses ambientes, geralmente baseados em containers. Com elas, você consegue criar, implantar, migrar e muito mais, de um ambiente para outro, de maneira muito eficiente, rápida e segura.

Inteligência Artificial

A Inteligência Artificial continuará em alta, pois cada vez mais empresas estão utilizando ferramentas de IA para melhorar suas operações.

Hoje, podemos dar vários exemplos do uso de ferramentas de IA que impactam em nosso dia a dia. Um exemplo bem palpável é a popularização do uso de chatbots de atendimento ao cliente, que está sendo inserida para melhorar o suporte e rapidez nas informações. Outro exemplo é o algoritmo de reconhecimento facial que o Facebook utiliza para identificar pessoas em uma foto.

Entre as ferramentas de IA que vêm ficando mais populares e que continuarão em alta em 2019, nós podemos citar o TensorFlow. O TensorFlow é uma biblioteca de software muito poderosa para computação numérica usando grafos computacionais, sendo o principal software para desenvolvimento em Deep Learning e aplicações de Inteligência Artificial.

Outra ferramenta de IA que podemos citar e que vêm ganhando cada vez mais demanda pelo mercado de trabalho é o Azure Cognitive Services, da Microsoft. O Cognitive Services amplia o acesso de APIs de aprendizado de máquina.

Esses serviços permitem a criação de aplicativos inteligentes com algoritmos e funcionalidades avançadas (como reconhecimento de objetos em fotos, visão computacional, reconhecimento de fala e outras), podendo ser utilizados em apps, sites e bots para que eles entendam as necessidades do usuário, tudo isso sem precisar de conhecimentos específicos em IA. Todos estes serviços são oferecidos através de APIs e aplicações hospedadas no ambiente do Microsoft Azure, ou seja, através de Cloud Computing.

Frameworks de gestão

Apesar de não ser uma novidade, os frameworks de gestão sempre estarão em alta, pois o mercado em geral vem ficando cada vez mais alinhado com as boas práticas de gestão alinhadas com os processos de desenvolvimento.

Hoje, é imprescindível saber pelo menos um pouco sobre os principais frameworks como ITIL, SCRUM, Kanban e outros. Esses frameworks são importantes por estabelecerem boas práticas de TI no mundo corporativo, fazendo com que a gestão de projetos seja simples, dinâmica e com foco na melhoria geral dos resultados.

O ITIL é um dos frameworks mais adotados atualmente, tendo como foco o alinhamento dos serviços de TI aos objetivos do negócio. Após a sua adoção, os processos de TI tendem a ficar mais consistentes, além da maior eficácia na entrega dos serviços.

Visando ajudar o gerenciamento desses serviços, um outro framework também muito utilizado hoje em dia é o Scrum. Ele é muito aplicado a projetos de desenvolvimento de software, dividindo o trabalho em tarefas menores e incrementais, sempre monitoradas por meio de reuniões diárias. Tudo isso para que os objetivos sejam alcançados da melhor maneira possível.

Outro framework de gestão que vem se destacando cada vez mais é o Kanban. Ele é um quadro onde você sinaliza através de cartões ou post-its o andamento da sua tarefa, com a intenção de que todos os envolvidos do projeto saibam o que está acontecendo. É uma ótima ferramenta, pois além de ser bem visual, você consegue se manter atualizado rapidamente com relação ao andamento do projeto.

ITIL - Fundamentos - Parte 1
Curso de ITIL - Fundamentos - Parte 1
CONHEÇA O CURSO

Concluindo

Essas são algumas das tendências que continuam em alta nesse ano. Certamente, as melhores vagas na área de TI em 2019 exigirão que você conheça alguns dos tópicos que foram abordados. Aqui na TreinaWeb, além de cursos das tendências que abordamos neste artigo, também existem diversos cursos de desenvolvimento de software e muitos outros que vão fazer você alavancar sua carreira na área de TI.