サーチ…
前書き
コンストラクタインジェクション
オブジェクトはしばしば他のオブジェクトに依存します。コンストラクタで依存関係を作成する代わりに、依存関係をコンストラクタにパラメータとして渡す必要があります。これにより、オブジェクト間の密結合が確実に行われ、クラスのインスタンス化の依存関係を変更できるようになります。これには、依存関係を明示的にすることによってコードを読みやすくすることや、依存関係を簡単に切り換えて簡単に擬似的にテストできるようにするなど、多くの利点があります。
次の例では、 Component
はLogger
インスタンスに依存しますが、作成しません。代わりにコンストラクタに引数として渡す必要があります。
interface Logger {
public function log(string $message);
}
class Component {
private $logger;
public function __construct(Logger $logger) {
$this->logger = $logger;
}
}
依存性注入がなければ、コードはおそらく次のようになります。
class Component {
private $logger;
public function __construct() {
$this->logger = new FooLogger();
}
}
new
を使用してコンストラクタ内に新しいオブジェクトを作成すると、依存関係注入が使用されなかった(または不完全に使用された)こと、およびコードが密接に結合されていることが示されます。また、コードが不完全にテストされているか、プログラム状態に関する誤った前提を作る脆弱なテストがある可能性があるという兆候です。
上記の例では、依存関係注入を代わりに使用している場合、必要になった場合に別のLoggerに簡単に変更できます。たとえば、別の場所にログを記録する、または異なるログ形式を使用する、またはファイルではなくデータベースにログを記録するLogger実装を使用することがあります。
セッター注入
依存関係はセッターによって注入することもできます。
interface Logger {
public function log($message);
}
class Component {
private $logger;
private $databaseConnection;
public function __construct(DatabaseConnection $databaseConnection) {
$this->databaseConnection = $databaseConnection;
}
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
public function core() {
$this->logSave();
return $this->databaseConnection->save($this);
}
public function logSave() {
if ($this->logger) {
$this->logger->log('saving');
}
}
}
これは、クラスのコア機能が依存関係に依存しない場合に特に興味深いものです。
ここで必要な唯一の依存関係はDatabaseConnection
ため、コンストラクタ内にあります。 Logger
依存関係はオプションであるため、コンストラクタの一部である必要はなく、クラスを使いやすくします。
セッター注入を使用する場合は、機能を置き換えるのではなく機能を拡張する方がよいことに注意してください。依存関係を設定するとき、依存関係がある時点で変更されないことを確認することは何もないため、予期しない結果につながる可能性があります。たとえば、 FileLogger
を最初に設定し、 MailLogger
を設定することができます。これにより、カプセル化が中断され、ログを見つけるのが難しくなります。依存関係を置き換えるためです。
これを防ぐために、setter注入を使用して依存関係を追加する必要があります。
interface Logger {
public function log($message);
}
class Component {
private $loggers = array();
private $databaseConnection;
public function __construct(DatabaseConnection $databaseConnection) {
$this->databaseConnection = $databaseConnection;
}
public function addLogger(Logger $logger) {
$this->loggers[] = $logger;
}
public function core() {
$this->logSave();
return $this->databaseConnection->save($this);
}
public function logSave() {
foreach ($this->loggers as $logger) {
$logger->log('saving');
}
}
}
このように、コア機能を使用するたびに、ロガー依存関係が追加されていなくても中断されず、別のロガーを追加できたとしても、追加されたロガーが使用されます。機能を置き換える代わりに機能を拡張し ています。
コンテナ注入
Dependency Injection Container(DIC)を使用するコンテキストでのDependency Injection(DI)は、コンストラクタインジェクションのスーパーセットと見ることができます。 DICは、通常、クラスのコンストラクタの型ヒントを分析し、そのニーズを解決し、インスタンスの実行に必要な依存関係を効果的に注入します。
正確な実装はこのドキュメントの範囲をはるかに越えていますが、DICは非常に心強いです.DICはクラスの署名を使用しています...
namespace Documentation;
class Example
{
private $meaning;
public function __construct(Meaning $meaning)
{
$this->meaning = $meaning;
}
}
...自動的にインスタンス化するために、 オートローディングシステムでほとんどの時間を頼りにしています 。
// older PHP versions
$container->make('Documentation\Example');
// since PHP 5.5
$container->make(\Documentation\Example::class);
バージョン5.5以上でPHPを使用していて、上に示したようにクラスの名前を取得したい場合、正しい方法は2番目の方法です。そうすれば、最新のIDEを使用してクラスの使用法をすばやく見つけることができ、潜在的なリファクタリングに大きく役立ちます。あなたは普通の文字列に頼ってはいけません。
この場合、 Documentation\Example
それがMeaning
必要としていることを知り、DICはMeaning
タイプをインスタンス化します。具体的な実装は、消費するインスタンスに依存する必要はありません。
その代わりに、オブジェクト作成の前に、必要に応じて特定の型をインスタンス化する方法を指示するルールをコンテナに設定します。
これには多くの利点があります。
- 共通インスタンスを共有する
- 型シグネチャを解決するためのファクトリを提供する
- インタフェースの署名を解決する
特定のタイプをどのように管理する必要があるかについてのルールを定義すると、どのタイプを共有、インスタンス化、または工場から作成するかを細かく制御できます。