(jp) =
あなたは自動車業界にいて、オンデマンドで車を作る仕事をしています。 あなたの中のオブジェクト指向プログラマーは、「問題ありません。必要な数の車を作るために使用できる青写真を作成します!」と言います。
class Car
public function drive()
この車が動くためには、エンジンと車輪が必要です。 現在、その目標を達成するためのいくつかのアプローチがあります。 たとえば、次のことができます。
class Car
public function __construct()
$this->engine = new Engine();
$this->wheels = [
new Wheel(), new Wheel(),
new Wheel(), new Wheel(),
];
public function drive() ...
あなたが作るすべての車の青写真があります! 次に、上司があなたのところに来て、新しいクライアントがいて、電気自動車が欲しいと言いました。
だからあなたはこれをすることになります。
class ElectricCar extends Car
public function __construct()
parent::__construct();
$this->engine = new ElectricEngine();
「美しく解決された」—あなたは思います。 もちろん、呼び出し時に作成される冗長な通常のエンジンがあります parent::__construct()
、しかし、少なくとも車輪を再利用することができます!
これがどこに向かっているのかがわかると思います。 次のクライアントは派手なホイール カバー付きの車を希望し、別のクライアントは同じホイール カバー付きのディーゼル エンジンを希望し、別のクライアントはレース カーを要求し、最後のクライアントは自動運転車を希望しています。
ああ、自分でボートを作るためのエンジンを購入したいクライアントもいましたが、あなたは上司にそれは不可能だと言いました.
しばらくすると、オフィスには大量の設計図があり、それぞれが非常に具体的な車のバリエーションを説明しています。 整然と並べられた青写真の山から始めました。 しかし、しばらくすると、探している設計図を見つけるのに時間がかかりすぎたため、それらを別のフォルダーやボックスにグループ化する必要がありました。
オブジェクト指向プログラマーはしばしばこの継承の罠に陥り、コードベースが完全にめちゃくちゃになってしまいます。 それでは、より良いアプローチを見てみましょう。 「継承よりも合成」という言葉を聞いたことがあるでしょうか。
継承に対する構成は、基本クラスまたは親クラスからの継承ではなく、クラスが構成によってポリモーフィックな動作とコードの再利用を実現するという原則です—ウィキペディア
それはたくさんの流行語です。 車の例を見てみましょう。 原則は、 Car
は、他のクラスで構成されることによって、そのポリモーフィックな動作を実現する必要があります。
言葉 多形的な 文字通り「多くの形」を意味し、それを意味します Car
できるはずです drive
使用されるコンテキストに応じて、さまざまな方法で使用されます。
と コードの再利用、コードを再利用可能にしようとしています。 何十ものクラスがほぼ同じことをすることにはなりません。
# これは依存性注入と何の関係がありますか?
車のすべての可能なバリエーションを説明する独自の設計図を作成する代わりに、 Car
1つのことをして、それをうまくやる:ドライブ。
これは、エンジンがどのように構築されているか、どのホイールが取り付けられているかなど、車の関心事であってはならないことを意味します。 次のことだけを知っている必要があります。
動くエンジンと 4 つの車輪があれば、私は運転できます!
私たちはそれを順番に言うことができます Car
働くこと、それ ニーズ エンジンと車輪。 言い換えると: Car
依存する Engine
とのコレクション Wheels
.
それらの依存関係は 与えられた 車に。 または、別の言い方をすれば、注射されました。
class Car
public function __construct(
Engine $engine,
array $wheels
)
$this->engine = $engine;
$this->wheels = $wheels;
public function drive()
$this->engine->connectTo($this->wheels);
$this->engine->start();
$this->engine->accelerate();
レースカーが欲しいですか? 問題ない!
$raceCar = new Car(new TurboEngine(), [
new RacingWheel(), new RacingWheel(),
new RacingWheel(), new RacingWheel(),
]);
特別なホイールカバーを欲しがっていたクライアント? あなたはそれをカバーしました!
$smugCar = new Car(new Engine(), [
new FancyWheel(), new FancyWheel(),
new FancyWheel(), new FancyWheel(),
]);
あなたが持っている 多くの より柔軟になりました!
依存性注入とは、クラス自体が責任を負うのではなく、クラスに外部から要件を与えるという考え方です。
# 依存性注入ではないもの
この単純な原則に基づいて構築された、次のレベルに進むフレームワークとツールがあります。 たとえば、次のようなことを以前に聞いたことがあるかもしれません。
# 共有依存関係
依存関係を注入することの最も有益な副作用の 1 つは、外部のコンテキストが依存関係を制御できることです。 これは、クラスの同じインスタンスを、そのクラスに依存する他の複数のインスタンスに与えることができることを意味します。
共有または再利用可能な依存関係は、「依存関係の注入」というラベルが付けられることが最も多いものです。 確かに非常に良い習慣ですが、依存関係を共有することは、実際には依存関係注入の中心的な意味ではありません。
# 依存コンテナー
正確な名前ではありませんが、「制御の反転」コンテナと呼ばれることもあります。
正確な名前が何であれ、コンテナは一連のクラス定義です。 これは、アプリケーション内のオブジェクトを他の依存関係で構築する方法を知っている大きな箱です。 このようなコンテナには間違いなく多くのユースケースがありますが、依存性注入を行う必要はありません。
# 自動配線
開発者にさらに柔軟性を与えるために、一部のコンテナーでは、自動的に決定されるスマートなクラス定義が可能です。 これは、すべてのクラスの構築方法を手動で記述する必要がないことを意味します。 これらのコンテナはコードをスキャンし、タイプ ヒントとドキュメント ブロックを調べて必要な依存関係を判断します。
ここで多くの魔法が起こりますが、自動配線は迅速なアプリケーション開発に役立つツールです。
# サービスの場所
クラスに依存関係を注入する代わりに、クラスがコンテナーに「別のクラスのインスタンスを与える」ように要求できるようにするツールとフレームワークがいくつかあります。
クラスは特定の依存関係を構築する方法を知る必要がないため、これは最初は有益に思えるかもしれません。 ただし、クラスが独自のアカウントで依存関係を要求できるようにすることで、振り出しに戻ります。
サービスの場所を機能させるには、クラスが外部のシステムについて知る必要があります。 通話と大差ない new
クラス自体で。 この考え方は、実際には依存性注入が達成しようとするものとは逆です。 コンテナの本来の目的の誤用です。
# すべて注入
実際のプロジェクトでは、依存性注入がそうではないことに気付くでしょう。 いつも あなたの問題の解決策。
すべてのメリットには限界があることを認識することが重要です。 実用的なアプローチが有効な場合があるため、これを極端に行っていないことに常に注意する必要があります。 は より良い解決策。
# 最後に
依存性注入の背後にある核となる考え方は非常に単純ですが、より保守しやすく、テストしやすく、分離されたコードを書くことができます。
力強い柄だからこそ、その周りにツールがたくさん出てくるのは当然です。 その上に構築されたツールを使用する前に、まず基本的な原則を理解することは良いことだと思います。 そして、このブログ投稿がその一助になれば幸いです。
共有したい考えが頭に浮かんだ場合は、お気軽に次の方法でご連絡ください。 ツイッター または電子メール。
また、Reddit の /u/ImSuperObjective2 と私の同僚にも感謝します セバスチャン この投稿を読んで証明するために。