saya pencipta Dependency Injector . Ini adalah kerangka kerja injeksi ketergantungan untuk Python.
Ini adalah panduan definitif untuk membangun aplikasi dengan Dependency Injector. Tutorial sebelumnya membahas bagaimana membangun aplikasi web dengan Flask , REST API dengan Aiohttp, dan memantau daemon dengan Asyncio menggunakan injeksi ketergantungan.
Hari ini saya ingin menunjukkan bagaimana Anda dapat membangun aplikasi konsol (CLI).
Selain itu, saya telah menyiapkan jawaban atas pertanyaan yang sering diajukan dan akan menerbitkan catatan tambahannya.
Manual terdiri dari bagian-bagian berikut:
- Apa yang akan kita bangun?
- Mempersiapkan lingkungan
- Struktur proyek
- Menginstal dependensi
- Perlengkapan
- Wadah
- Bekerja dengan csv
- Bekerja dengan sqlite
- Pemilih Penyedia
- Tes
- Kesimpulan
- PS: tanya jawab
Proyek yang telah selesai dapat ditemukan di Github .
Untuk memulai, Anda harus memiliki:
- Python 3.5+
- Lingkungan virtual
Dan diinginkan untuk memiliki pemahaman umum tentang prinsip injeksi ketergantungan.
Apa yang akan kita bangun?
Kami akan membangun aplikasi CLI (konsol) yang mencari film. Sebut saja Movie Lister.
Bagaimana cara kerja Movie Lister?
- Kami memiliki database film
- Informasi berikut ini diketahui tentang setiap film:
- Nama
- Tahun penerbitan
- Nama sutradara
- Basis data didistribusikan dalam dua format:
- File csv
- Database persegi
- Aplikasi mencari database menggunakan kriteria berikut:
- Nama sutradara
- Tahun penerbitan
- Format database lain mungkin ditambahkan di masa mendatang
Movie Lister adalah contoh aplikasi yang digunakan dalam artikel Martin Fowler tentang injeksi ketergantungan dan kontrol inversi.
Seperti inilah diagram kelas dari aplikasi Movie Lister:
Tanggung jawab antar kelas didistribusikan sebagai berikut:
MovieLister- bertanggung jawab atas pencarianMovieFinder- Bertanggung jawab untuk mengekstraksi data dari databaseMovie- "film" kelas entitas
Mempersiapkan lingkungan
Mari kita mulai dengan mempersiapkan lingkungan.
Pertama-tama, kita perlu membuat folder proyek dan lingkungan virtual:
mkdir movie-lister-tutorial
cd movie-lister-tutorial
python3 -m venv venv
Sekarang mari aktifkan lingkungan virtual:
. venv/bin/activate
Lingkungan sudah siap. Sekarang mari masuk ke dalam struktur proyek.
Struktur proyek
Pada bagian ini, kami akan mengatur struktur proyek.
Mari buat struktur berikut di folder saat ini. Biarkan semua file kosong untuk saat ini.
Struktur awal:
./
├── movies/
│ ├── __init__.py
│ ├── __main__.py
│ └── containers.py
├── venv/
├── config.yml
└── requirements.txt
Menginstal dependensi
Saatnya menginstal dependensi. Kami akan menggunakan paket seperti ini:
dependency-injector- kerangka injeksi ketergantunganpyyaml- perpustakaan untuk mem-parsing file YAML, digunakan untuk membaca konfigurasipytest- kerangka pengujianpytest-cov- Perpustakaan pembantu untuk mengukur cakupan kode dengan tes
Mari tambahkan baris berikut ke file
requirements.txt:
dependency-injector
pyyaml
pytest
pytest-cov
Dan jalankan di terminal:
pip install -r requirements.txt
Instalasi dependensi selesai. Pindah ke perlengkapan.
Perlengkapan
Di bagian ini, kami akan menambahkan perlengkapan. Data uji disebut perlengkapan.
Kami akan membuat skrip yang akan membuat database uji.
Tambahkan direktori
data/ke root proyek dan tambahkan file di dalamnya fixtures.py:
./
├── data/
│ └── fixtures.py
├── movies/
│ ├── __init__.py
│ ├── __main__.py
│ └── containers.py
├── venv/
├── config.yml
└── requirements.txt
Selanjutnya, edit
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()
Sekarang mari kita jalankan di terminal:
python data/fixtures.py
Skrip harus menghasilkan
OKkesuksesan.
Kami memverifikasi bahwa file
movies.csvdan movies.dbmuncul di direktori data/:
./
├── data/
│ ├── fixtures.py
│ ├── movies.csv
│ └── movies.db
├── movies/
│ ├── __init__.py
│ ├── __main__.py
│ └── containers.py
├── venv/
├── config.yml
└── requirements.txt
Jadwal dibuat. Ayo lanjutkan.
Wadah
Di bagian ini, kami akan menambahkan bagian utama dari aplikasi kami - wadah.
Penampung memungkinkan Anda mendeskripsikan struktur aplikasi dalam gaya deklaratif. Ini akan berisi semua komponen aplikasi dan dependensinya. Semua dependensi akan ditentukan secara eksplisit. Penyedia digunakan untuk menambahkan komponen aplikasi ke wadah. Penyedia mengontrol masa pakai komponen. Saat membuat penyedia, tidak ada komponen yang dibuat. Kami memberi tahu penyedia cara membuat objek, dan itu akan membuatnya segera setelah diperlukan. Jika ketergantungan satu penyedia adalah penyedia lain, maka itu akan dipanggil dan seterusnya di sepanjang rantai ketergantungan.
Mari edit
containers.py:
"""Containers module."""
from dependency_injector import containers
class ApplicationContainer(containers.DeclarativeContainer):
...
Wadahnya masih kosong. Kami akan menambahkan penyedia di bagian selanjutnya.
Mari tambahkan fungsi lain
main(). Tanggung jawabnya adalah menjalankan aplikasi. Untuk saat ini, dia hanya akan membuat wadah.
Mari edit
__main__.py:
"""Main module."""
from .containers import ApplicationContainer
def main():
container = ApplicationContainer()
if __name__ == '__main__':
main()
Kontainer adalah objek pertama dalam aplikasi. Ini digunakan untuk mendapatkan semua objek lainnya.
Bekerja dengan csv
Sekarang mari tambahkan semua yang kita butuhkan untuk bekerja dengan file csv.
Kita butuh:
- Intinya
Movie - Kelas dasar
MovieFinder - Implementasinya
CsvMovieFinder - Kelas
MovieLister
Setelah menambahkan setiap komponen, kami akan menambahkannya ke wadah.
Buat file
entities.pydalam sebuah paket movies:
./
├── data/
│ ├── fixtures.py
│ ├── movies.csv
│ └── movies.db
├── movies/
│ ├── __init__.py
│ ├── __main__.py
│ ├── containers.py
│ └── entities.py
├── venv/
├── config.yml
└── requirements.txt
dan tambahkan baris berikut di dalamnya:
"""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),
)
Sekarang kita perlu menambahkan pabrik
Movieke wadah. Untuk ini kita membutuhkan modul providersdari dependency_injector.
Mari edit
containers.py:
"""Containers module."""
from dependency_injector import containers, providers
from . import entities
class ApplicationContainer(containers.DeclarativeContainer):
movie = providers.Factory(entities.Movie)
Jangan lupa untuk menghapus elipsis ( ...). Penampung sudah memiliki penyedia dan tidak lagi diperlukan.
Mari lanjutkan ke pembuatan
finders.
Buat file
finders.pydalam sebuah paket movies:
./
├── data/
│ ├── fixtures.py
│ ├── movies.csv
│ └── movies.db
├── movies/
│ ├── __init__.py
│ ├── __main__.py
│ ├── containers.py
│ ├── entities.py
│ └── finders.py
├── venv/
├── config.yml
└── requirements.txt
dan tambahkan baris berikut di dalamnya:
"""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]
Sekarang mari tambahkan
CsvMovieFinderke wadah.
Mari edit
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,
)
Anda
CsvMovieFindermemiliki ketergantungan pada pabrik Movie. CsvMovieFindermembutuhkan pabrik karena akan membuat objek Moviesaat membaca data dari file. Untuk melewati pabrik, kami menggunakan atribut .provider. Ini disebut delegasi penyedia. Jika kita menetapkan pabrik moviesebagai ketergantungan, itu akan dipanggil saat csv_finderdibuat CsvMovieFinderdan objek akan diteruskan sebagai injeksi Movie. Menggunakan atribut .providersebagai injeksi akan diteruskan oleh penyedia itu sendiri.
Ini
csv_finderjuga memiliki ketergantungan pada beberapa opsi konfigurasi. Kami telah menambahkan penyedia onfigurationuntuk meneruskan dependensi ini.
Kami menggunakan parameter konfigurasi sebelum menetapkan nilainya. Ini adalah prinsip yang digunakan oleh penyediaConfiguration.
Pertama kita gunakan, lalu kita atur nilainya.
Sekarang mari tambahkan nilai konfigurasi.
Mari edit
config.yml:
finder:
csv:
path: "data/movies.csv"
delimiter: ","
Nilai ditetapkan ke file konfigurasi. Mari perbarui fungsi
main()untuk menunjukkan lokasinya.
Mari edit
__main__.py:
"""Main module."""
from .containers import ApplicationContainer
def main():
container = ApplicationContainer()
container.config.from_yaml('config.yml')
if __name__ == '__main__':
main()
Ayo pergi ke
listers.
Buat file
listers.pydalam sebuah paket 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
dan tambahkan baris berikut di dalamnya:
"""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
]
Kami memperbarui
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,
)
Semua komponen dibuat dan ditambahkan ke penampung.
Terakhir, kami memperbarui fungsinya
main().
Mari edit
__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()
Semuanya sudah siap. Sekarang mari luncurkan aplikasinya.
Mari kita jalankan di terminal:
python -m movies
Kamu akan lihat:
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')]
Aplikasi kami bekerja dengan database film di
csv. Kami juga perlu menambahkan dukungan format sqlite. Kami akan membahas ini di bagian selanjutnya.
Bekerja dengan sqlite
Di bagian ini, kami akan menambahkan tipe lain
MovieFinder- SqliteMovieFinder.
Mari edit
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]
Tambahkan penyedia
sqlite_finderke container dan tentukan sebagai dependensi untuk penyedia lister.
Mari edit
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,
)
Penyedia
sqlite_findermemiliki ketergantungan pada opsi konfigurasi yang belum kami tentukan. Mari perbarui file konfigurasi:
Edit
config.yml:
finder:
csv:
path: "data/movies.csv"
delimiter: ","
sqlite:
path: "data/movies.db"
Selesai. Mari kita periksa.
Kami mengeksekusi di terminal:
python -m movies
Kamu akan lihat:
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')]
Aplikasi kami mendukung kedua format database:
csvdan sqlite. Setiap kali kami perlu mengubah format, kami harus mengubah kode di wadah. Kami akan meningkatkan ini di bagian selanjutnya.
Pemilih Penyedia
Di bagian ini, kami akan membuat aplikasi kami lebih fleksibel.
Anda tidak perlu lagi mengubah kode untuk beralih di antara
csvdan sqliteformat. Kami akan menerapkan sakelar berdasarkan variabel lingkungan MOVIE_FINDER_TYPE:
- Saat
MOVIE_FINDER_TYPE=csvaplikasi menggunakancsv. - Saat
MOVIE_FINDER_TYPE=sqliteaplikasi menggunakansqlite.
Penyedia akan membantu kami dalam hal ini
Selector. Ini memilih penyedia berdasarkan opsi konfigurasi ( dokumentasi ).
Mari edit
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,
)
Kami membuat penyedia
finderdan menetapkannya sebagai dependensi untuk penyedia lister. Penyedia findermemilih antara penyedia csv_finderdan sqlite_findersaat waktu proses. Pilihannya tergantung pada nilai sakelar.
Sakelar adalah opsi konfigurasi
config.finder.type. Saat nilainya csvdigunakan oleh penyedia dari kunci csv. Demikian juga untuk sqlite.
Sekarang kita perlu membaca nilai
config.finder.typedari variabel lingkungan MOVIE_FINDER_TYPE.
Mari edit
__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()
Selesai.
Jalankan perintah berikut di terminal:
MOVIE_FINDER_TYPE=csv python -m movies
MOVIE_FINDER_TYPE=sqlite python -m movies
Output untuk setiap perintah akan terlihat seperti ini:
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')]
Di bagian ini, kami berkenalan dengan penyedia
Selector. Dengan penyedia ini, Anda dapat membuat aplikasi Anda lebih fleksibel. Nilai sakelar dapat disetel dari sumber apa pun: file konfigurasi, kamus, penyedia lain.
Petunjuk:
Mengganti nilai konfigurasi dari penyedia lain memungkinkan Anda mengimplementasikan overloading konfigurasi dalam aplikasi Anda tanpa hot restart.
Untuk melakukan ini, Anda perlu menggunakan delegasi penyedia dan.override().
Di bagian selanjutnya, kami akan menambahkan beberapa tes.
Tes
Terakhir, mari tambahkan beberapa tes.
Buat file
tests.pydalam sebuah paket 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
dan tambahkan baris berikut ke dalamnya:
"""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'
Sekarang mari kita mulai menguji dan memeriksa cakupannya:
pytest movies/tests.py --cov=movies
Kamu akan lihat:
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%
Kami menggunakan metode.override()penyediafinder. Penyedia diganti dengan tiruan. Saat menghubungi penyedia,findertiruan yang menimpa sekarang akan dikembalikan.
Pekerjaan sudah selesai. Sekarang mari kita rangkum.
Kesimpulan
Kami membangun aplikasi CLI menggunakan prinsip injeksi ketergantungan. Kami menggunakan Dependency Injector sebagai kerangka kerja injeksi ketergantungan.
Keuntungan yang Anda dapatkan dengan Dependency Injector adalah wadah.
Penampung mulai terbayar saat Anda perlu memahami atau mengubah struktur aplikasi Anda. Dengan container, ini mudah karena semua komponen aplikasi dan dependensinya ditentukan secara eksplisit di satu tempat:
"""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,
)
Sebuah wadah sebagai peta aplikasi Anda. Anda selalu tahu apa yang tergantung pada apa.
PS: tanya jawab
Dalam komentar pada tutorial sebelumnya, pertanyaan keren telah diajukan: "mengapa ini perlu?", "Mengapa kita membutuhkan kerangka kerja?", "Bagaimana kerangka kerja membantu dalam implementasi?"
Saya sudah menyiapkan jawaban:
Apa itu injeksi ketergantungan?
- Ini adalah prinsip yang mengurangi kopling dan meningkatkan kohesi
Mengapa saya harus menggunakan injeksi ketergantungan?
- kode Anda menjadi lebih fleksibel, mudah dipahami, dan lebih dapat diuji
- Anda memiliki lebih sedikit masalah saat Anda perlu memahami cara kerjanya atau mengubahnya
Bagaimana cara mulai menerapkan injeksi ketergantungan?
- Anda mulai menulis kode mengikuti prinsip injeksi ketergantungan
- Anda mendaftarkan semua komponen dan dependensinya di container
- saat Anda membutuhkan sebuah komponen, Anda mendapatkannya dari penampung
Mengapa saya membutuhkan kerangka kerja untuk ini?
- Anda membutuhkan kerangka kerja agar tidak membuatnya sendiri. Kode pembuatan objek akan digandakan dan sulit diubah. Untuk menghindarinya, Anda membutuhkan wadah.
- kerangka kerja memberi Anda wadah dan penyedia
- penyedia mengontrol masa benda. Anda akan membutuhkan objek pabrik, lajang, dan konfigurasi
- penampung berfungsi sebagai kumpulan penyedia
Berapa harga yang saya bayar?
- Anda perlu menentukan dependensi di container secara eksplisit
- ini adalah pekerjaan tambahan
- itu akan mulai membayar dividen saat proyek mulai tumbuh
- atau 2 minggu setelah penyelesaian (ketika Anda lupa keputusan apa yang Anda buat dan bagaimana struktur proyek)
Konsep Dependency Injector
Selain itu, saya akan menjelaskan konsep Dependency Injector sebagai kerangka kerja.
Dependency Injector didasarkan pada dua prinsip:
- Eksplisit lebih baik daripada implisit (PEP20).
- Jangan lakukan sihir apa pun dengan kode Anda.
Apa yang membedakan Dependency Injector dari framework lain?
- Tidak ada tautan otomatis. Kerangka tidak secara otomatis menghubungkan dependensi. Introspeksi, menghubungkan dengan nama argumen dan / atau tipe tidak digunakan. Karena "eksplisit lebih baik daripada implisit (PEP20)".
- Tidak mencemari kode aplikasi Anda. Aplikasi Anda tidak menyadari dan tidak bergantung pada Dependency Injector. Tidak ada
@injectdekorator, anotasi, tambalan atau trik sulap lainnya.
Dependency Injector menawarkan kontrak sederhana:
- Anda menunjukkan kerangka bagaimana mengumpulkan objek
- Kerangka mengumpulkan mereka
Kekuatan dari Dependency Injector terletak pada kesederhanaan dan keterusterangannya. Ini adalah alat sederhana untuk menerapkan prinsip yang ampuh.
Apa berikutnya?
Jika Anda tertarik, tetapi ragu, rekomendasi saya adalah ini:
Coba pendekatan ini selama 2 bulan. Dia tidak intuitif. Perlu waktu untuk terbiasa dan merasa. Manfaatnya menjadi nyata saat proyek berkembang hingga 30+ komponen dalam satu wadah. Jika Anda tidak menyukainya, jangan rugi banyak. Jika Anda suka, dapatkan keuntungan yang signifikan.
- Pelajari lebih lanjut tentang Dependency Injector di GitHub
- Lihat dokumentasinya di Read the Docs
Saya akan senang menerima umpan balik dan menjawab pertanyaan di komentar.