web-dev-qa-db-ja.com

PHP OOP依存性注入-「新しい」ステートメントを使用するのが適切な場合

クラスまたは関数の「新しい」ステートメントを回避するように言われましたが、プログラムのルートにあるクラスからオブジェクトを作成し(おそらくDIコンテナーを使用して)、オブジェクトをクラスに注入しました。

とにかく、この「ルール」が成り立たないケースが(大多数)あると思います。この例を見てみましょう:

<?php
$mailerobject = new PHPMailer();

class send_orderconfirmation {

    public function __construct(Mailer $mailer) {
        $this->mailer = $mailer;
    }

    public function send_orderconfirmation() {
        $email = "[email protected]";
        $prename = "Enrique";
        $name = "Iglesias";
        $subject = "Your blue pills order";

        $this->mailer->addAddress("$email", "$prename $name");
        $this->mailer->Subject = $subject;
    }
}

?>
  • まず最初に、いつsend_orderconfirmationオブジェクトを作成しますか?これをプログラムのルートで作成することは意味がありません。新しい注文を処理し、今度は確認を送信したいクラスによって作成されるべきであるというのは、むしろ論理的ではありませんか?ただし、新しいステートメントはこのクラスに含まれる必要があります。
  • 2つ目はさらに重要です。上記の例では、メーラーオブジェクトのセッターを使用して、送信する電子メールに関するデータ(件名、アドレス、受信者名)を入力するとすぐに、オブジェクトがこれで「汚染」されます。データ。したがって、mailerobjectを必要とする次の関数は、オブジェクトを使用するために、まずオブジェクトをクリーンアップする必要があります。また、同時に2つのメーラーオブジェクトを持つことはできません。 2つのメーラーオブジェクトに同時にデータを入力する場合。

何が欠けていますか?クラスでオブジェクトを作成する正当な理由はありますか、それとも常に挿入する必要がありますか?

4
Benedikt

もちろん、オブジェクトをどこかに作成する必要があります。理論的には、アプリケーションは2つの部分に分かれています。

  • ビジネスロジックの部分-楽しい部分、クラスを一緒に使用して動作のルールを作成、
  • 工場-必要な悪、オブジェクトを作成して一緒に配線するためにクラスがnewedされる魔法の場所。

ここで、ビジネスロジックの部分がデカップリングが重要であり、 ルーズカップリング が有利です。 しかし、なぜ正確なのですか?

1つの(単純な)理由:同じAPIを持つ新しいものが優れている(高速でリソースのコストが少ないなど)場合に、依存関係(モジュール)を切り替えることができるようにするため。

データベースを見てください。 [〜#〜] orm [〜#〜] レイヤーをいくつか使用して、そのために adapters を作成できます。そのため、ビジネスレイヤーはORMに完全に直接依存していませんが、あなたの抽象化。

ORMレイヤーを使用したい場合はいつでも、ORMレイヤーをコードで直接newすることができますが、ある日により良い代替策があると判断した場合はどうなりますか? ORMレイヤーオブジェクトが作成されたすべてのクラスを見つけて置き換える以外に選択肢はなく、異なるORMにはわずかに異なるAPI(public 1回または非常に数回だけnewedオブジェクトを注入して再利用する可能性が高くなります。それを複数のクラスに分け、それらすべてがORMレイヤーにアクセスする必要があります。複数の場所で古いnewを新しいものに手動で置き換える必要がないだけでなく、ほとんどの場合1つだけでなく、アダプターを使用することで、ビジネスレイヤーにまったく触れる必要がなくなるでしょう。

以前は数時間かかっていた新しいORMを使用するタスクを数分で(つまり、テストに必要な時間なしで)実行できるようになりました。これは驚くべきことです。

ビジネスレイヤーで直接newingクラスを使用しない唯一のポイントは、分離を提供することです(newキーワードはアプリケーションのファクトリー部分に委任されます)が、すべてを分離しようとするのは意味がありません。そうしようとすると、アプリケーションが工場地獄に変わるだけです。

クラスを直接作成するか、作成(またはインスタンスを取得するプロセス)をファクトリに委任するかを決定する際の経験則は、デカップリングが大きなメリットをもたらすかどうかを理解することです。大きなアプリケーションを小さなモジュール(これらのモジュールの一部は、キャッシュ、データベース、ドメイン、httpなど)の合計として考える場合、クラスを直接newingする前に、次のような可能性があるかどうかを考える必要があります。いつか新しいバージョンが来て、それがより良くなり、(潜在的に)使用したいと思うかもしれません。答えが「はい」の場合、依存関係を挿入し、手動で作成しないでください。

その他の非常に重要なことは、すべてのアプリケーションが依存関係の注入に依存し、プログラマーがそれに依存している場合、クラスが外部の世界(データベース、Webサービス)と通信するときに依存関係が注入されることです。このクラスを使用する開発者は、依存関係を調べるだけでクラスが外部データと通信して変更する可能性があることを直接認識しています。これは驚くべきことです。これは、クラスに必要なAPIが(必要なオブジェクトを直接作成することによって)嫌いだからです。外部からの依存関係を求めています。

依存関係が外部である場合(Composer、C#の場合はNuGet、Javaの場合はMaven)などのパッケージマネージャーを使用してプルする)、独自の抽象化の背後にある実際の依存関係を隠す手間を省く必要があります。そして、あなたのアプリケーションを抽象化、あなたが設計したインターフェース、そしてそれがどのように機能するか、そしてあなたがそれから何を期待できるかについて非常によく知っているインターフェースに依存するようにしてください(もう一度アダプターパターン)。

一方、データアクセスレイヤーとドメイン(ビジネスレイヤー)の間に中間レイヤーを提供し、ドメインエンティティを純粋なデータに、またはその逆に転送するリポジトリを想定しています。ドメインは(通常)明確に定義されたルールを持つかなり厳密なレイヤーであり、クラスは最終製品であり、ビジネスルールが変更された場合にのみ置き換えられる可能性があります。そのような場合、通常、そのようなエンティティを作成する方法は1つしかなく、ほとんどの場合、ファクトリを作成せずに、オブジェクトを直接new作成しても問題ありません。

特定の例を見ると、newオブジェクトを挿入するという正しい方向に進んでいます。新しいMailerが一度出現し、現在のMailerよりも優れている可能性は十分にあります(ただし、同じ名前で同じ引数をとるメソッドがあります)。前に述べたように、今度は単に別のクラスをnewして、代わりにそのクラスを注入する必要があります。

次に、2つの引数、送信者のアドレスと名前を使用してaddAddressメソッドを呼び出しますが、addAddressUserEmailのようなデータオブジェクトを受け取ったとしても、 newUserEmailクラスに直接send_orderconfirmationUserEmailは単純なメッセンジャーである可能性が非常に高いため、関連するデータをグループ化してメソッドに必要な引数を少なくします。

すべてを注入することは実際には意味のないことであり、実際に許可されているだけでなく、実際にオブジェクトを直接作成するほうが良い場合もあります。誰かがそれに反対している場合は、理由を尋ねる必要があります。意味のある説明を提供できない場合は、見栄えが良いという理由だけで、デザインパターンをあちこちに投げて、アプリケーションを過剰設計している可能性があります。

残念なことに、プログラミングではEdgeのケースが多すぎて、数千ページよりも短い本で説明することは不可能であり、それでもおそらくそれらすべてをカバーすることはできません。その上、実際に理解しようとしているのは、アーキテクチャの問題であり、これらに対する解決策は、いくつかのオンラインチュートリアル(新しい言語を学びたいときにできること)を読むことから来ることはめったになく、高齢者の経験から得られます。

この特定のケースだけでなく、一般的なソフトウェア開発の場合、経験を積んで、何が機能し、何が機能しないかを理解する必要があります(ユーザーまたは環境のいずれか)。おそらく誰かがすでに数年のプログラミングをスキップしてそれを学ぶためにアプリケーションを設計する方法の回避策を作成しているかもしれません。私自身は15年少し前にプログラミングを始め、経験を積むことによって難しい方法でそれを学ぶ必要がありました。

6
Andy

newの使用を避ける理由は、プログラムを「ステッチ解除」して変更できる seam を作成するためです。コードのその部分を編集せずに動作。

そのため、「新規」の使用を避けるかどうかを決定するには、作成しているコードを、さまざまな種類の使用、または使用とテストの両方のために、さまざまな方法で実行する可能性が高いかどうかを考える必要があります。

あなたの特定の例を考えると、私はおそらく同じクラスで_send_orderconfirmation_を作成しません。モック_send_orderconfirmation_またはのインスタンスのいずれかでそれを使用するクラスをテストするためです実際にメールを送信しないように構成された実際の_send_orderconfirmation_クラス。

PHPMailerに優れたインターフェースがあるとは思いません-ステートフルであるため、依存性注入サービスとして使用するのは困難です。これを引き続き使用したい場合は、_send_orderconfirmation_に注入できる新しいステートレスサービスクラスにラップします。単一のメソッドpublic function getPHPMailer(): PHPMailerを含むPHPMailerFactoryクラス、または単一のメソッドpublic fuction sendEmail()を含むMailerクラス。後者を実行する場合は、すべてのデータを一度に 'sendEmail'に渡す方法を見つける必要があります。Emailクラスを作成します。これは、どこでもnewを呼び出すか、配列を渡します。または、いくつかのパラメータをsendEmailに渡します。

0
bdsl