sou o criador do injetor de dependĂȘncia . Esta Ă© uma estrutura de injeção de dependĂȘncia para Python.
Este Ă© o guia definitivo para criar aplicativos com o injetor de dependĂȘncia. Tutoriais anteriores cobrem como construir um aplicativo da web com Flask , API REST com Aiohttp e monitorar um daemon com Asyncio usando injeção de dependĂȘncia.
Hoje eu quero mostrar como vocĂȘ pode construir um aplicativo de console (CLI).
Além disso, preparei respostas para as perguntas mais frequentes e publicarei seu pós-escrito.
O manual consiste nas seguintes partes:
- O que vamos construir?
- Preparando o ambiente
- Estrutura do projeto
- Instalando dependĂȘncias
- LuminĂĄrias
- Recipiente
- Trabalhando com csv
- Trabalhando com sqlite
- Provider Selector
- Testes
- ConclusĂŁo
- PS: perguntas e respostas
O projeto concluĂdo pode ser encontrado no Github .
Para começar vocĂȘ deve ter:
- Python 3.5+
- Ambiente virtual
E Ă© desejĂĄvel ter uma compreensĂŁo geral do princĂpio da injeção de dependĂȘncia.
O que vamos construir?
Estaremos construindo um aplicativo CLI (console) que procura filmes. Vamos chamĂĄ-lo de Movie Lister.
Como funciona o Movie Lister?
- Temos um banco de dados de filmes
- As seguintes informaçÔes são conhecidas sobre cada filme:
- Nome
- Ano de emissĂŁo
- Nome do diretor
- O banco de dados Ă© distribuĂdo em dois formatos:
- Arquivo csv
- Banco de dados Sqlite
- O aplicativo pesquisa o banco de dados usando os seguintes critérios:
- Nome do diretor
- Ano de emissĂŁo
- Outros formatos de banco de dados podem ser adicionados no futuro
Movie Lister Ă© um aplicativo de amostra usado no artigo de Martin Fowler sobre injeção de dependĂȘncia e inversĂŁo de controle.
Esta Ă© a aparĂȘncia do diagrama de classes do aplicativo Movie Lister:
As responsabilidades entre as classes sĂŁo distribuĂdas da seguinte forma:
MovieLister- responsĂĄvel pela buscaMovieFinder- responsĂĄvel por extrair dados do banco de dadosMovie- classe de entidade "filme"
Preparando o ambiente
Vamos começar preparando o ambiente.
Em primeiro lugar, precisamos criar uma pasta de projeto e um ambiente virtual:
mkdir movie-lister-tutorial
cd movie-lister-tutorial
python3 -m venv venv
Agora vamos ativar o ambiente virtual:
. venv/bin/activate
O ambiente estĂĄ pronto. Agora vamos entrar na estrutura do projeto.
Estrutura do projeto
Nesta seção, vamos organizar a estrutura do projeto.
Vamos criar a seguinte estrutura na pasta atual. Deixe todos os arquivos vazios por enquanto.
Estrutura inicial:
./
âââ movies/
â âââ __init__.py
â âââ __main__.py
â âââ containers.py
âââ venv/
âââ config.yml
âââ requirements.txt
Instalando dependĂȘncias
Ă hora de instalar as dependĂȘncias. Usaremos pacotes como este:
dependency-injector- framework de injeção de dependĂȘnciapyyaml- biblioteca para analisar arquivos YAML, usada para ler a configuraçãopytest- estrutura de testepytest-cov- biblioteca auxiliar para medir a cobertura de cĂłdigo por testes
Vamos adicionar as seguintes linhas ao arquivo
requirements.txt:
dependency-injector
pyyaml
pytest
pytest-cov
E execute no terminal:
pip install -r requirements.txt
A instalação das dependĂȘncias foi concluĂda. Passando para os acessĂłrios.
LuminĂĄrias
Nesta seção, adicionaremos acessórios. Os dados de teste são chamados de acessórios.
Criaremos um script que criarĂĄ bancos de dados de teste.
Adicione um diretĂłrio
data/Ă raiz do projeto e adicione um arquivo dentro de fixtures.py:
./
âââ data/
â âââ fixtures.py
âââ movies/
â âââ __init__.py
â âââ __main__.py
â âââ containers.py
âââ venv/
âââ config.yml
âââ requirements.txt
A seguir, edite
fixtures.py:
"""Fixtures module."""
import csv
import sqlite3
import pathlib
SAMPLE_DATA = [
('The Hunger Games: Mockingjay - Part 2', 2015, 'Francis Lawrence'),
('Rogue One: A Star Wars Story', 2016, 'Gareth Edwards'),
('The Jungle Book', 2016, 'Jon Favreau'),
]
FILE = pathlib.Path(__file__)
DIR = FILE.parent
CSV_FILE = DIR / 'movies.csv'
SQLITE_FILE = DIR / 'movies.db'
def create_csv(movies_data, path):
with open(path, 'w') as opened_file:
writer = csv.writer(opened_file)
for row in movies_data:
writer.writerow(row)
def create_sqlite(movies_data, path):
with sqlite3.connect(path) as db:
db.execute(
'CREATE TABLE IF NOT EXISTS movies '
'(title text, year int, director text)'
)
db.execute('DELETE FROM movies')
db.executemany('INSERT INTO movies VALUES (?,?,?)', movies_data)
def main():
create_csv(SAMPLE_DATA, CSV_FILE)
create_sqlite(SAMPLE_DATA, SQLITE_FILE)
print('OK')
if __name__ == '__main__':
main()
Agora vamos executar no terminal:
python data/fixtures.py
O script deve sair
OKcom sucesso.
Verificamos se os arquivos
movies.csve movies.dbapareceram no diretĂłrio data/:
./
âââ data/
â âââ fixtures.py
â âââ movies.csv
â âââ movies.db
âââ movies/
â âââ __init__.py
â âââ __main__.py
â âââ containers.py
âââ venv/
âââ config.yml
âââ requirements.txt
Jogos sĂŁo criados. Vamos continuar.
Recipiente
Nesta seção, adicionaremos a parte principal de nosso aplicativo - o contĂȘiner.
O contĂȘiner permite que vocĂȘ descreva a estrutura do aplicativo em um estilo declarativo. Ele conterĂĄ todos os componentes do aplicativo e suas dependĂȘncias. Todas as dependĂȘncias serĂŁo especificadas explicitamente. Os provedores sĂŁo usados ââpara adicionar componentes do aplicativo ao contĂȘiner. Os provedores controlam a vida Ăștil dos componentes. Ao criar um provedor, nenhum componente Ă© criado. Dizemos ao provedor como criar o objeto e ele o criarĂĄ assim que necessĂĄrio. Se a dependĂȘncia de um provedor for de outro provedor, ele serĂĄ chamado e assim por diante ao longo da cadeia de dependĂȘncias.
Vamos editar
containers.py:
"""Containers module."""
from dependency_injector import containers
class ApplicationContainer(containers.DeclarativeContainer):
...
O recipiente ainda estå vazio. Adicionaremos provedores nas próximas seçÔes.
Vamos adicionar outra função
main(). Sua responsabilidade Ă© executar o aplicativo. Por enquanto, ela criarĂĄ apenas um container.
Vamos editar
__main__.py:
"""Main module."""
from .containers import ApplicationContainer
def main():
container = ApplicationContainer()
if __name__ == '__main__':
main()
O contĂȘiner Ă© o primeiro objeto do aplicativo. Ă usado para obter todos os outros objetos.
Trabalhando com csv
Agora vamos adicionar tudo o que precisamos para trabalhar com arquivos csv.
NĂłs precisamos:
- A essĂȘncia
Movie - Classe base
MovieFinder - Sua implementação
CsvMovieFinder - Classe
MovieLister
ApĂłs adicionar cada componente, iremos adicionĂĄ-lo ao contĂȘiner.
Crie um arquivo
entities.pyem um pacote movies:
./
âââ data/
â âââ fixtures.py
â âââ movies.csv
â âââ movies.db
âââ movies/
â âââ __init__.py
â âââ __main__.py
â âââ containers.py
â âââ entities.py
âââ venv/
âââ config.yml
âââ requirements.txt
e adicione as seguintes linhas dentro:
"""Movie entities module."""
class Movie:
def __init__(self, title: str, year: int, director: str):
self.title = str(title)
self.year = int(year)
self.director = str(director)
def __repr__(self):
return '{0}(title={1}, year={2}, director={3})'.format(
self.__class__.__name__,
repr(self.title),
repr(self.year),
repr(self.director),
)
Agora precisamos adicionar uma fĂĄbrica
Movieao contĂȘiner. Para isso, precisamos de um mĂłdulo providersde dependency_injector.
Vamos editar
containers.py:
"""Containers module."""
from dependency_injector import containers, providers
from . import entities
class ApplicationContainer(containers.DeclarativeContainer):
movie = providers.Factory(entities.Movie)
NĂŁo se esqueça de remover as reticĂȘncias ( ...). O container jĂĄ tem provedores e nĂŁo Ă© mais necessĂĄrio.
Vamos prosseguir para a criação
finders.
Crie um arquivo
finders.pyem um pacote movies:
./
âââ data/
â âââ fixtures.py
â âââ movies.csv
â âââ movies.db
âââ movies/
â âââ __init__.py
â âââ __main__.py
â âââ containers.py
â âââ entities.py
â âââ finders.py
âââ venv/
âââ config.yml
âââ requirements.txt
e adicione as seguintes linhas dentro:
"""Movie finders module."""
import csv
from typing import Callable, List
from .entities import Movie
class MovieFinder:
def __init__(self, movie_factory: Callable[..., Movie]) -> None:
self._movie_factory = movie_factory
def find_all(self) -> List[Movie]:
raise NotImplementedError()
class CsvMovieFinder(MovieFinder):
def __init__(
self,
movie_factory: Callable[..., Movie],
path: str,
delimiter: str,
) -> None:
self._csv_file_path = path
self._delimiter = delimiter
super().__init__(movie_factory)
def find_all(self) -> List[Movie]:
with open(self._csv_file_path) as csv_file:
csv_reader = csv.reader(csv_file, delimiter=self._delimiter)
return [self._movie_factory(*row) for row in csv_reader]
Agora vamos adicionar
CsvMovieFinderao contĂȘiner.
Vamos editar
containers.py:
"""Containers module."""
from dependency_injector import containers, providers
from . import finders, entities
class ApplicationContainer(containers.DeclarativeContainer):
config = providers.Configuration()
movie = providers.Factory(entities.Movie)
csv_finder = providers.Singleton(
finders.CsvMovieFinder,
movie_factory=movie.provider,
path=config.finder.csv.path,
delimiter=config.finder.csv.delimiter,
)
VocĂȘ
CsvMovieFindertem uma dependĂȘncia da fĂĄbrica Movie. CsvMovieFinderprecisa de uma fĂĄbrica, pois criarĂĄ objetos Movieao ler os dados de um arquivo. Para passar de fĂĄbrica, usamos o atributo .provider. Isso Ă© chamado de delegação do provedor. Se especificarmos uma fĂĄbrica moviecomo dependĂȘncia, ela serĂĄ chamada quando csv_finderfor criada CsvMovieFindere um objeto serĂĄ passado como uma injeção Movie. O uso do atributo .providercomo uma injeção serĂĄ passado pelo prĂłprio provedor.
Ele
csv_findertambĂ©m depende de vĂĄrias opçÔes de configuração. Adicionamos um provedor onfigurationpara passar essas dependĂȘncias.
Usamos os parĂąmetros de configuração antes de definir seus valores. Este Ă© o princĂpio pelo qual o provedor trabalhaConfiguration.
Primeiro usamos, depois definimos os valores.
Agora vamos adicionar os valores de configuração.
Vamos editar
config.yml:
finder:
csv:
path: "data/movies.csv"
delimiter: ","
Os valores são definidos para o arquivo de configuração. Vamos atualizar a função
main()para indicar sua localização.
Vamos editar
__main__.py:
"""Main module."""
from .containers import ApplicationContainer
def main():
container = ApplicationContainer()
container.config.from_yaml('config.yml')
if __name__ == '__main__':
main()
Vamos para
listers.
Crie um arquivo
listers.pyem um pacote movies:
./
âââ data/
â âââ fixtures.py
â âââ movies.csv
â âââ movies.db
âââ movies/
â âââ __init__.py
â âââ __main__.py
â âââ containers.py
â âââ entities.py
â âââ finders.py
â âââ listers.py
âââ venv/
âââ config.yml
âââ requirements.txt
e adicione as seguintes linhas dentro:
"""Movie listers module."""
from .finders import MovieFinder
class MovieLister:
def __init__(self, movie_finder: MovieFinder):
self._movie_finder = movie_finder
def movies_directed_by(self, director):
return [
movie for movie in self._movie_finder.find_all()
if movie.director == director
]
def movies_released_in(self, year):
return [
movie for movie in self._movie_finder.find_all()
if movie.year == year
]
NĂłs atualizamos
containers.py:
"""Containers module."""
from dependency_injector import containers, providers
from . import finders, listers, entities
class ApplicationContainer(containers.DeclarativeContainer):
config = providers.Configuration()
movie = providers.Factory(entities.Movie)
csv_finder = providers.Singleton(
finders.CsvMovieFinder,
movie_factory=movie.provider,
path=config.finder.csv.path,
delimiter=config.finder.csv.delimiter,
)
lister = providers.Factory(
listers.MovieLister,
movie_finder=csv_finder,
)
Todos os componentes sĂŁo criados e adicionados ao contĂȘiner.
Finalmente, atualizamos a função
main().
Vamos editar
__main__.py:
"""Main module."""
from .containers import ApplicationContainer
def main():
container = ApplicationContainer()
container.config.from_yaml('config.yml')
lister = container.lister()
print(
'Francis Lawrence movies:',
lister.movies_directed_by('Francis Lawrence'),
)
print(
'2016 movies:',
lister.movies_released_in(2016),
)
if __name__ == '__main__':
main()
Tudo estĂĄ pronto. Agora vamos iniciar o aplicativo.
Vamos executar no terminal:
python -m movies
VocĂȘ verĂĄ:
Francis Lawrence movies: [Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')]
2016 movies: [Movie(title='Rogue One: A Star Wars Story', year=2016, director='Gareth Edwards'), Movie(title='The Jungle Book', year=2016, director='Jon Favreau')]
Nosso aplicativo funciona com um banco de dados de filmes em
csv. Também precisamos adicionar suporte de formato sqlite. Trataremos disso na próxima seção.
Trabalhando com sqlite
Nesta seção, adicionaremos outro tipo
MovieFinder- SqliteMovieFinder.
Vamos editar
finders.py:
"""Movie finders module."""
import csv
import sqlite3
from typing import Callable, List
from .entities import Movie
class MovieFinder:
def __init__(self, movie_factory: Callable[..., Movie]) -> None:
self._movie_factory = movie_factory
def find_all(self) -> List[Movie]:
raise NotImplementedError()
class CsvMovieFinder(MovieFinder):
def __init__(
self,
movie_factory: Callable[..., Movie],
path: str,
delimiter: str,
) -> None:
self._csv_file_path = path
self._delimiter = delimiter
super().__init__(movie_factory)
def find_all(self) -> List[Movie]:
with open(self._csv_file_path) as csv_file:
csv_reader = csv.reader(csv_file, delimiter=self._delimiter)
return [self._movie_factory(*row) for row in csv_reader]
class SqliteMovieFinder(MovieFinder):
def __init__(
self,
movie_factory: Callable[..., Movie],
path: str,
) -> None:
self._database = sqlite3.connect(path)
super().__init__(movie_factory)
def find_all(self) -> List[Movie]:
with self._database as db:
rows = db.execute('SELECT title, year, director FROM movies')
return [self._movie_factory(*row) for row in rows]
Adicione o provedor
sqlite_finderao contĂȘiner e especifique-o como uma dependĂȘncia para o provedor lister.
Vamos editar
containers.py:
"""Containers module."""
from dependency_injector import containers, providers
from . import finders, listers, entities
class ApplicationContainer(containers.DeclarativeContainer):
config = providers.Configuration()
movie = providers.Factory(entities.Movie)
csv_finder = providers.Singleton(
finders.CsvMovieFinder,
movie_factory=movie.provider,
path=config.finder.csv.path,
delimiter=config.finder.csv.delimiter,
)
sqlite_finder = providers.Singleton(
finders.SqliteMovieFinder,
movie_factory=movie.provider,
path=config.finder.sqlite.path,
)
lister = providers.Factory(
listers.MovieLister,
movie_finder=sqlite_finder,
)
O provedor
sqlite_finderdepende de opçÔes de configuração que ainda não definimos. Vamos atualizar o arquivo de configuração:
Editar
config.yml:
finder:
csv:
path: "data/movies.csv"
delimiter: ","
sqlite:
path: "data/movies.db"
Feito. Vamos checar.
Executamos no terminal:
python -m movies
VocĂȘ verĂĄ:
Francis Lawrence movies: [Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')]
2016 movies: [Movie(title='Rogue One: A Star Wars Story', year=2016, director='Gareth Edwards'), Movie(title='The Jungle Book', year=2016, director='Jon Favreau')]
Nosso aplicativo suporta os dois formatos de banco de dados:
csve sqlite. Cada vez que precisamos mudar o formato, temos que mudar o código no container. Vamos melhorar isso na próxima seção.
Provider Selector
Nesta seção, tornaremos nosso aplicativo mais flexĂvel.
VocĂȘ nĂŁo precisarĂĄ mais fazer alteraçÔes no cĂłdigo para alternar entre os formatos
csve sqlite. Implementaremos um switch com base em uma variĂĄvel de ambiente MOVIE_FINDER_TYPE:
- Quando um
MOVIE_FINDER_TYPE=csvaplicativo usa ocsv. - Quando um
MOVIE_FINDER_TYPE=sqliteaplicativo usa osqlite.
O provedor vai nos ajudar com isso
Selector. Ele escolhe um provedor com base na opção de configuração ( documentação ).
Vamos editar
containers.py:
"""Containers module."""
from dependency_injector import containers, providers
from . import finders, listers, entities
class ApplicationContainer(containers.DeclarativeContainer):
config = providers.Configuration()
movie = providers.Factory(entities.Movie)
csv_finder = providers.Singleton(
finders.CsvMovieFinder,
movie_factory=movie.provider,
path=config.finder.csv.path,
delimiter=config.finder.csv.delimiter,
)
sqlite_finder = providers.Singleton(
finders.SqliteMovieFinder,
movie_factory=movie.provider,
path=config.finder.sqlite.path,
)
finder = providers.Selector(
config.finder.type,
csv=csv_finder,
sqlite=sqlite_finder,
)
lister = providers.Factory(
listers.MovieLister,
movie_finder=finder,
)
Criamos um provedor
findere o especificamos como uma dependĂȘncia para o provedor lister. O provedor finderescolhe entre provedores csv_findere sqlite_finderem tempo de execução. A escolha depende do valor do switch.
O switch é a opção de configuração
config.finder.type. Quando seu valor Ă© csvusado pelo provedor da chave csv. Da mesma forma para sqlite.
Agora precisamos ler o valor
config.finder.typeda variĂĄvel de ambiente MOVIE_FINDER_TYPE.
Vamos editar
__main__.py:
"""Main module."""
from .containers import ApplicationContainer
def main():
container = ApplicationContainer()
container.config.from_yaml('config.yml')
container.config.finder.type.from_env('MOVIE_FINDER_TYPE')
lister = container.lister()
print(
'Francis Lawrence movies:',
lister.movies_directed_by('Francis Lawrence'),
)
print(
'2016 movies:',
lister.movies_released_in(2016),
)
if __name__ == '__main__':
main()
Feito.
Execute os seguintes comandos no terminal:
MOVIE_FINDER_TYPE=csv python -m movies
MOVIE_FINDER_TYPE=sqlite python -m movies
A saĂda de cada comando serĂĄ assim:
Francis Lawrence movies: [Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')]
2016 movies: [Movie(title='Rogue One: A Star Wars Story', year=2016, director='Gareth Edwards'), Movie(title='The Jungle Book', year=2016, director='Jon Favreau')]
Nesta seção, conhecemos o provedor
Selector. Com esse provedor, vocĂȘ pode tornar seu aplicativo mais flexĂvel. O valor da chave pode ser definido a partir de qualquer fonte: arquivo de configuração, dicionĂĄrio, outro provedor.
Dica:
Substituir um valor de configuração de outro provedor permite que vocĂȘ implemente a sobrecarga de configuração em seu aplicativo sem uma reinicialização a quente.
Para fazer isso, vocĂȘ precisa usar a delegação de provedor e o.override().
Na próxima seção, adicionaremos alguns testes.
Testes
Finalmente, vamos adicionar alguns testes.
Crie um arquivo
tests.pyem um pacote movies:
./
âââ data/
â âââ fixtures.py
â âââ movies.csv
â âââ movies.db
âââ movies/
â âââ __init__.py
â âââ __main__.py
â âââ containers.py
â âââ entities.py
â âââ finders.py
â âââ listers.py
â âââ tests.py
âââ venv/
âââ config.yml
âââ requirements.txt
e adicione as seguintes linhas a ele:
"""Tests module."""
from unittest import mock
import pytest
from .containers import ApplicationContainer
@pytest.fixture
def container():
container = ApplicationContainer()
container.config.from_dict({
'finder': {
'type': 'csv',
'csv': {
'path': '/fake-movies.csv',
'delimiter': ',',
},
'sqlite': {
'path': '/fake-movies.db',
},
},
})
return container
def test_movies_directed_by(container):
finder_mock = mock.Mock()
finder_mock.find_all.return_value = [
container.movie('The 33', 2015, 'Patricia Riggen'),
container.movie('The Jungle Book', 2016, 'Jon Favreau'),
]
with container.finder.override(finder_mock):
lister = container.lister()
movies = lister.movies_directed_by('Jon Favreau')
assert len(movies) == 1
assert movies[0].title == 'The Jungle Book'
def test_movies_released_in(container):
finder_mock = mock.Mock()
finder_mock.find_all.return_value = [
container.movie('The 33', 2015, 'Patricia Riggen'),
container.movie('The Jungle Book', 2016, 'Jon Favreau'),
]
with container.finder.override(finder_mock):
lister = container.lister()
movies = lister.movies_released_in(2015)
assert len(movies) == 1
assert movies[0].title == 'The 33'
Agora vamos começar a testar e verificar a cobertura:
pytest movies/tests.py --cov=movies
VocĂȘ verĂĄ:
platform darwin -- Python 3.8.3, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
plugins: cov-2.10.0
collected 2 items
movies/tests.py .. [100%]
---------- coverage: platform darwin, python 3.8.3-final-0 -----------
Name Stmts Miss Cover
------------------------------------------
movies/__init__.py 0 0 100%
movies/__main__.py 10 10 0%
movies/containers.py 9 0 100%
movies/entities.py 7 1 86%
movies/finders.py 26 13 50%
movies/listers.py 8 0 100%
movies/tests.py 24 0 100%
------------------------------------------
TOTAL 84 24 71%
Usamos o mĂ©todo do.override()provedorfinder. O provedor Ă© substituĂdo por mock. Ao entrar em contato com o provedor, afindersimulação de substituição agora serĂĄ retornada.
O trabalho estĂĄ feito. Agora vamos resumir.
ConclusĂŁo
ConstruĂmos um aplicativo CLI usando o princĂpio de injeção de dependĂȘncia. Usamos o Dependency Injector como uma estrutura de injeção de dependĂȘncia.
A vantagem que vocĂȘ obtĂ©m com o Dependency Injector Ă© o contĂȘiner.
O contĂȘiner começa a pagar quando vocĂȘ precisa entender ou alterar a estrutura do seu aplicativo. Com um contĂȘiner, isso Ă© fĂĄcil, porque todos os componentes do aplicativo e suas dependĂȘncias sĂŁo explicitamente definidos em um sĂł lugar:
"""Containers module."""
from dependency_injector import containers, providers
from . import finders, listers, entities
class ApplicationContainer(containers.DeclarativeContainer):
config = providers.Configuration()
movie = providers.Factory(entities.Movie)
csv_finder = providers.Singleton(
finders.CsvMovieFinder,
movie_factory=movie.provider,
path=config.finder.csv.path,
delimiter=config.finder.csv.delimiter,
)
sqlite_finder = providers.Singleton(
finders.SqliteMovieFinder,
movie_factory=movie.provider,
path=config.finder.sqlite.path,
)
finder = providers.Selector(
config.finder.type,
csv=csv_finder,
sqlite=sqlite_finder,
)
lister = providers.Factory(
listers.MovieLister,
movie_finder=finder,
)
Um contĂȘiner como um mapa de seu aplicativo. VocĂȘ sempre sabe o que depende do quĂȘ.
PS: perguntas e respostas
Nos comentårios do tutorial anterior, perguntas interessantes foram feitas: "por que isso é necessårio?", "Por que precisamos de um framework?", "Como o framework ajuda na implementação?"
Preparei as respostas:
O que Ă© injeção de dependĂȘncia?
- Ă© o princĂpio que reduz o acoplamento e aumenta a coesĂŁo
Por que devo usar injeção de dependĂȘncia?
- seu cĂłdigo se torna mais flexĂvel, compreensĂvel e melhor testĂĄvel
- vocĂȘ tem menos problemas quando precisa entender como funciona ou alterĂĄ-lo
Como começo a aplicar injeção de dependĂȘncia?
- vocĂȘ começa a escrever cĂłdigo seguindo o princĂpio de injeção de dependĂȘncia
- vocĂȘ registra todos os componentes e suas dependĂȘncias no contĂȘiner
- quando vocĂȘ precisa de um componente, vocĂȘ o obtĂ©m do contĂȘiner
Por que preciso de uma estrutura para isso?
- vocĂȘ precisa de uma estrutura para nĂŁo criar a sua prĂłpria. O cĂłdigo de criação do objeto serĂĄ duplicado e difĂcil de alterar. Para evitar isso, vocĂȘ precisa de um contĂȘiner.
- a estrutura fornece um contĂȘiner e provedores
- os provedores controlam o tempo de vida dos objetos. VocĂȘ precisarĂĄ de fĂĄbricas, singletons e objetos de configuração
- o contĂȘiner serve como uma coleção de provedores
Que preço estou pagando?
- vocĂȘ precisa especificar explicitamente as dependĂȘncias no contĂȘiner
- este Ă© um trabalho adicional
- começarå a pagar dividendos quando o projeto começar a crescer
- ou 2 semanas apĂłs sua conclusĂŁo (quando vocĂȘ esquecer quais decisĂ”es vocĂȘ tomou e qual Ă© a estrutura do projeto)
Conceito de injetor de dependĂȘncia
AlĂ©m disso, descreverei o conceito de injetor de dependĂȘncia como uma estrutura.
O injetor de dependĂȘncia Ă© baseado em dois princĂpios:
- ExplĂcito Ă© melhor do que implĂcito (PEP20).
- Não faça mågica com seu código.
Como o Dependency Injector Ă© diferente de outras estruturas?
- Sem vinculação automĂĄtica. A estrutura nĂŁo vincula dependĂȘncias automaticamente. A introspecção, vinculação por nomes de argumento e / ou tipos nĂŁo Ă© usada. Porque "explĂcito Ă© melhor do que implĂcito (PEP20)".
- NĂŁo polui o cĂłdigo do seu aplicativo. Seu aplicativo desconhece e Ă© independente do Injetor de DependĂȘncias. Sem
@injectdecoradores, anotaçÔes, remendos ou outros truques de mågica.
O Dependency Injector oferece um contrato simples:
- VocĂȘ mostra ao framework como coletar objetos
- A estrutura os coleta
A força do Injetor de DependĂȘncia estĂĄ em sua simplicidade e objetividade. Ă uma ferramenta simples para implementar um princĂpio poderoso.
Qual Ă© o prĂłximo?
Se vocĂȘ estiver interessado, mas hesite, minha recomendação Ă© esta:
tente esta abordagem por 2 meses. Ele nĂŁo Ă© intuitivo. Leva tempo para se acostumar e sentir. Os benefĂcios se tornam tangĂveis quando o projeto cresce para mais de 30 componentes em um contĂȘiner. Se vocĂȘ nĂŁo gosta, nĂŁo perca muito. Se vocĂȘ gostar, obtenha uma vantagem significativa.
- Saiba mais sobre o Dependency Injector no GitHub
- Confira a documentação em Read the Docs
Eu ficaria feliz em receber feedback e responder perguntas nos comentĂĄrios.