CLIアプリケーション+ディペンデンシーインジェクター-ディペンデンシーインジェクションガイド+ FAQ

こんにちは、



私はDependencyInjectorの作成者ですこれは、Pythonの依存関係注入フレームワークです。



これは、DependencyInjectorを使用してアプリケーションを構築するための最も信頼のおけるガイドです。過去のチュートリアルでは、構築する方法を網羅フラスコを使用したWebアプリケーションAiohttpとREST APIを、そしてAsyncioでデーモンを監視依存性の注入を使用しました。



今日は、コンソール(CLI)アプリケーションを構築する方法を示したいと思います。



また、よくある質問への回答を用意し、追記を公開します。



マニュアルは次の部分で構成されています。



  1. 何を構築しますか?
  2. 環境の準備
  3. プロジェクト構造
  4. 依存関係のインストール
  5. 備品
  6. コンテナ
  7. csvの操作
  8. sqliteの操作
  9. プロバイダーセレクター
  10. テスト
  11. 結論
  12. PS:質問と回答


完成したプロジェクトはGithubにあります。



開始するには、次のものが必要です。



  • Python 3.5+
  • 仮想環境


また、依存関係の注入の原理を一般的に理解していることが望ましいです。



何を構築しますか?



映画を探すCLI(コンソール)アプリケーションを構築します。それをMovieListerと呼びましょう。



Movie Listerはどのように機能しますか?



  • 映画のデータベースがあります
  • 各フィルムについて、次の情報がわかっています。

    • 名前
    • 発行年
    • 監督の名前
  • データベースは2つの形式で配布されます。

    • Csvファイル
    • Sqliteデータベース
  • アプリケーションは、次の基準を使用してデータベースを検索します。

    • 監督の名前
    • 発行年
  • 他のデータベース形式は将来追加される可能性があります


Movie Listerは、依存関係の挿入と制御の反転に関するMartinFowler記事使用されているサンプルアプリケーションです



MovieListerアプリケーションのクラス図は





次のようになります。クラス間の責任は次のように分散されます。



  • MovieLister -検索を担当
  • MovieFinder -データベースからデータを抽出する責任があります
  • Movie -エンティティクラス「ムービー」


環境の準備



環境の準備から始めましょう。



まず、プロジェクトフォルダと仮想環境を作成する必要があります。



mkdir movie-lister-tutorial
cd movie-lister-tutorial
python3 -m venv venv


次に、仮想環境をアクティブ化します。



. venv/bin/activate


環境の準備ができています。それでは、プロジェクトの構造に取り掛かりましょう。



プロジェクト構造



このセクションでは、プロジェクトの構造を整理します。



現在のフォルダに次の構造を作成しましょう。今のところ、すべてのファイルを空のままにします。



初期構造:



./
├── movies/
│   ├── __init__.py
│   ├── __main__.py
│   └── containers.py
├── venv/
├── config.yml
└── requirements.txt


依存関係のインストール



依存関係をインストールする時が来ました。次のようなパッケージを使用します。



  • dependency-injector -依存関係注入フレームワーク
  • pyyaml -構成の読み取りに使用されるYAMLファイルを解析するためのライブラリ
  • pytest -テストフレームワーク
  • pytest-cov -テストによってコードカバレッジを測定するためのヘルパーライブラリ


次の行をファイルに追加しましょうrequirements.txt



dependency-injector
pyyaml
pytest
pytest-cov


そして、ターミナルで実行します。



pip install -r requirements.txt


依存関係のインストールが完了しました。フィクスチャに移ります。



備品



このセクションでは、フィクスチャを追加します。テストデータはフィクスチャと呼ばれます。



テストデータベースを作成するスクリプトを作成します。プロジェクトのルートに



ディレクトリdata/を追加し、その中にファイルを追加しますfixtures.py



./
├── data/
│   └── fixtures.py
├── movies/
│   ├── __init__.py
│   ├── __main__.py
│   └── containers.py
├── venv/
├── config.yml
└── requirements.txt


次に、編集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()


それでは、ターミナルで実行してみましょう。



python data/fixtures.py


スクリプトはOK成功すると出力されます。



私たちは、そのファイルを確認movies.csvし、movies.dbディレクトリに登場しましたdata/



./
├── data/
│   ├── fixtures.py
│   ├── movies.csv
│   └── movies.db
├── movies/
│   ├── __init__.py
│   ├── __main__.py
│   └── containers.py
├── venv/
├── config.yml
└── requirements.txt


フィクスチャが作成されます。続けましょう。



コンテナ



このセクションでは、アプリケーションの主要部分であるコンテナを追加します。



コンテナを使用すると、アプリケーションの構造を宣言的なスタイルで記述することができます。これには、すべてのアプリケーションコンポーネントとその依存関係が含まれます。すべての依存関係は明示的に指定されます。プロバイダーは、アプリケーションコンポーネントをコンテナーに追加するために使用されます。プロバイダーは、コンポーネントの寿命を制御します。プロバイダーを作成する場合、コンポーネントは作成されません。オブジェクトの作成方法をプロバイダーに指示し、必要に応じてすぐに作成します。あるプロバイダーの依存関係が別のプロバイダーである場合、依存関係のチェーンに沿って呼び出されます。



編集しましょうcontainers.py



"""Containers module."""

from dependency_injector import containers


class ApplicationContainer(containers.DeclarativeContainer):
    ...


コンテナはまだ空です。次のセクションでプロバイダーを追加します。



別の関数を追加しましょうmain()彼女の責任は、アプリケーションを実行することです。今のところ、彼女はコンテナを作成するだけです。



編集しましょう__main__.py



"""Main module."""

from .containers import ApplicationContainer


def main():
    container = ApplicationContainer()


if __name__ == '__main__':
    main()


コンテナは、アプリケーションの最初のオブジェクトです。他のすべてのオブジェクトを取得するために使用されます。


csvの操作



次に、csvファイルを操作するために必要なすべてのものを追加しましょう。



必要なもの:



  • エッセンス Movie
  • 基本クラス MovieFinder
  • その実装 CsvMovieFinder
  • クラス MovieLister


各コンポーネントを追加した後、それをコンテナに追加します。パッケージに







ファイルentities.py作成しますmovies



./
├── data/
│   ├── fixtures.py
│   ├── movies.csv
│   └── movies.db
├── movies/
│   ├── __init__.py
│   ├── __main__.py
│   ├── containers.py
│   └── entities.py
├── venv/
├── config.yml
└── requirements.txt


中に次の行を追加します。



"""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),
        )


次にMovie、コンテナにファクトリを追加する必要があります。このためには、providersからのモジュールが必要ですdependency_injector



編集しましょうcontainers.py



"""Containers module."""

from dependency_injector import containers, providers

from . import entities

class ApplicationContainer(containers.DeclarativeContainer):

    movie = providers.Factory(entities.Movie)


省略記号(...を削除することを忘れないでくださいコンテナにはすでにプロバイダーが含まれているため、不要になりました。


の作成に移りましょうfindersパッケージに



ファイルfinders.py作成しますmovies



./
├── data/
│   ├── fixtures.py
│   ├── movies.csv
│   └── movies.db
├── movies/
│   ├── __init__.py
│   ├── __main__.py
│   ├── containers.py
│   ├── entities.py
│   └── finders.py
├── venv/
├── config.yml
└── requirements.txt


中に次の行を追加します。



"""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]


それではCsvMovieFinder、コンテナに追加ましょう



編集しましょう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,
    )


あなたはCsvMovieFinder、工場への依存性を持っていますMovieファイルからデータを読み取るときにCsvMovieFinderオブジェクトを作成するため、ファクトリが必要Movieです。工場を通過するために、属性を使用します.provider。これはプロバイダー委任と呼ばれます。movie依存関係としてファクトリを指定すると、ファクトリcsv_finderが作成されたときに呼び出さCsvMovieFinder、オブジェクトがインジェクションとして渡されMovieます。属性.providerをインジェクションとして使用すると、プロバイダー自体によって渡されます。



またcsv_finder、いくつかの構成オプションも依存しています。onfigurationこれらの依存関係を渡すためのプロバイダー追加しました



値を設定する前に、構成パラメーターを使用しました。これは、プロバイダーが機能する原則ですConfiguration



最初にを使用し、次に値を設定します。



次に、構成値を追加しましょう。



編集しましょうconfig.yml



finder:

  csv:
    path: "data/movies.csv"
    delimiter: ","


値は構成ファイルに設定されます。関数main()更新して、その場所を示しましょう



編集しましょう__main__.py



"""Main module."""

from .containers import ApplicationContainer


def main():
    container = ApplicationContainer()

    container.config.from_yaml('config.yml')


if __name__ == '__main__':
    main()


に行きましょうlistersパッケージに



ファイルlisters.py作成します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


中に次の行を追加します。



"""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
        ]


更新し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,
    )


すべてのコンポーネントが作成され、コンテナに追加されます。



最後に、関数を更新しますmain()



編集しましょう__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()


すべての準備が整いました。それでは、アプリケーションを起動しましょう。



ターミナルで実行してみましょう:



python -m movies


次のように表示されます。



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')]


私たちのアプリケーションは、の映画のデータベースで動作しますcsvまた、フォーマットサポートを追加する必要がありますsqliteこれについては、次のセクションで扱います。



sqliteの操作



このセクションでは、別のタイプを追加しますMovieFinder- SqliteMovieFinder



編集しましょう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]


プロバイダーsqlite_finderをコンテナーに追加し、プロバイダーの依存関係として指定しますlister



編集しましょう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,
    )


プロバイダーsqlite_finderは、まだ定義していない構成オプションに依存しています。構成ファイルを更新しましょう:



編集config.yml



finder:

  csv:
    path: "data/movies.csv"
    delimiter: ","

  sqlite:
    path: "data/movies.db"


完了。確認しよう。



ターミナルで実行します:



python -m movies


次のように表示されます。



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')]


私たちのアプリケーションは、両方のデータベース形式をサポートしていますcsvsqliteフォーマットを変更する必要があるたびに、コンテナ内のコードを変更する必要があります。これは次のセクションで改善します。



プロバイダーセレクター



このセクションでは、アプリケーションをより柔軟にします。



あなた間のスイッチにコードを変更することはもはや必要csvsqliteフォーマット。環境変数に基づいてスイッチを実装しますMOVIE_FINDER_TYPE



  • ときにMOVIE_FINDER_TYPE=csvアプリケーションが使用していますcsv
  • ときにMOVIE_FINDER_TYPE=sqliteアプリケーションが使用していますsqlite


プロバイダーがこれをお手伝いしますSelector構成オプション(ドキュメント)に基づいてプロバイダーを選択します



編集しましょう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,
    )


プロバイダーを作成し、プロバイダーfinderの依存関係として指定しましたlisterプロバイダーfinderは、プロバイダーcsv_findersqlite_finder実行時にプロバイダーを選択します。選択は、スイッチの値によって異なります。



スイッチは構成オプションconfig.finder.typeです。その値がcsvキーからプロバイダーによって使用される場合csv同様にsqlite



次にconfig.finder.type、環境変数から値を読み取る必要がありますMOVIE_FINDER_TYPE



編集しましょう__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()


完了。



ターミナルで次のコマンドを実行します。



MOVIE_FINDER_TYPE=csv python -m movies
MOVIE_FINDER_TYPE=sqlite python -m movies


各コマンドの出力は次のようになります。



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')]


このセクションでは、プロバイダーについて知りましたSelectorこのプロバイダーを使用すると、アプリケーションをより柔軟にすることができます。スイッチの値は、構成ファイル、辞書、その他のプロバイダーなど、任意のソースから設定できます。



ヒント:

別のプロバイダーからの構成値をオーバーライドすると、ホットリスタートなしでアプリケーションに構成のオーバーロードを実装できます。

これを行うには、プロバイダーの委任とを使用する必要があります.override()



次のセクションでは、いくつかのテストを追加します。



テスト



最後に、いくつかのテストを追加しましょう。パッケージに



ファイルtests.py作成します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


それに次の行を追加します。



"""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'


それでは、テストを開始してカバレッジを確認しましょう。



pytest movies/tests.py --cov=movies


次のように表示されます。



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%


.override()プロバイダー方式を使用しましたfinderプロバイダーはモックによってオーバーライドされます。プロバイダーに連絡すると、finderオーバーライドするモックが返されます。



作業は完了です。それでは要約しましょう。



結論



依存関係注入の原則を使用してCLIアプリケーションを構築しました。依存関係インジェクターを依存関係インジェクションフレームワークとして使用しました。



Dependency Injectorで得られる利点は、コンテナーです。



アプリケーションの構造を理解または変更する必要がある場合、コンテナは成果を上げ始めます。コンテナを使用すると、すべてのアプリケーションコンポーネントとその依存関係が一箇所で明示的に定義されるため、これは簡単です。



"""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,
    )




アプリケーションのマップとしてのコンテナー。あなたは常に何が何に依存するかを知っています。



PS:質問と回答



前のチュートリアルへのコメントでは、「なぜこれが必要なのか」、「なぜフレームワークが必要なのか」、「フレームワークは実装にどのように役立つのか」というクールな質問がありました。



私は答えを用意しました:



依存関係の注入とは何ですか?



  • 結合を減らし、凝集力を高めるのが原理です


なぜ依存関係の注入を使用する必要があるのですか?



  • コードがより柔軟で、理解しやすく、テストしやすくなります
  • それがどのように機能するかを理解したり、変更したりする必要がある場合は、問題が少なくなります


依存関係インジェクションの適用を開始するにはどうすればよいですか?



  • 依存関係の注入の原則に従ってコードを書き始めます
  • すべてのコンポーネントとその依存関係をコンテナに登録します
  • コンポーネントが必要な場合は、コンテナから取得します


なぜこのためのフレームワークが必要なのですか?



  • 独自のフレームワークを作成しないためには、フレームワークが必要です。オブジェクト作成コードは複製され、変更が困難になります。これを回避するには、コンテナが必要です。
  • フレームワークはあなたにコンテナとプロバイダーを提供します
  • プロバイダーはオブジェクトの存続期間を制御します。工場、シングルトン、構成オブジェクトが必要になります
  • コンテナはプロバイダーのコレクションとして機能します


いくら払っていますか?



  • コンテナ内の依存関係を明示的に指定する必要があります
  • これは追加の作業です
  • プロジェクトが成長し始めると、配当金の支払いが開始されます
  • または完了後2週間(どのような決定をしたか、プロジェクトの構造は何かを忘れた場合)


ディペンデンシーインジェクターのコンセプト



さらに、フレームワークとしてのDependencyInjectorの概念についても説明します。



依存関係インジェクターは、次の2つの原則に基づいています。



  • 明示的は暗黙的(PEP20)よりも優れています。
  • コードで魔法をかけないでください。


Dependency Injectorは他のフレームワークとどのように異なりますか?



  • 自動リンクはありません。フレームワークは依存関係を自動的にリンクしません。イントロスペクション、引数名および/またはタイプによるリンクは使用されません。「明示的は暗黙的(PEP20)よりも優れている」からです。
  • アプリケーションコードを汚染しません。アプリケーションは、依存関係インジェクターを認識せず、独立しています。@injectデコレータ、注釈、パッチ、その他の魔法のトリックはありません


Dependency Injectorは、単純な契約を提供します。



  • オブジェクトを収集する方法をフレームワークに示します
  • フレームワークはそれらを収集します


依存関係インジェクターの強みは、そのシンプルさとわかりやすさにあります。これは、強力な原則を実装するためのシンプルなツールです。



次は何ですか?



興味があるが躊躇している場合は、次のことをお勧めし



ます。このアプローチを2か月間試してください。彼は直感的ではありません。慣れて感じるには時間がかかります。プロジェクトがコンテナ内の30以上のコンポーネントに成長すると、メリットが明確になります。気に入らなくても、多くを失うことはありません。あなたがそれを好きなら、重要な利点を手に入れてください。





コメントでフィードバックを受け取り、質問に答えていただければ幸いです。



All Articles