私の会社が使用する内部フレームワークには、引数のないコンストラクターを持つクラスのメンバーとしてインスタンス化されるパターンを持つかなり重要なオブジェクトがありますが、使用するには、開始メソッドを呼び出す必要があります。コンストラクターは一般的にミニマリストですが、同時にstartを呼び出さなければならない使用できないオブジェクトを構築できるのはなぜですか?
基本的にこれ
public class Someclass{
FrameworkThingy thingy = new FrameworkThingy();
//Framework auto runs this method as part of Spring beans
public void startUpMethod() {
thingy.start();
// Why not just do the start stuff in the constructor?
}
}
私が過去にこのパターンを見たとき、それは常に次の理由の1つのためにありました:
あなたの状況についてこれ以上の詳細がなければ、私はあなたにもっと具体的な答えを与えることはできません。
ソフトウェア開発における多くの質問と同様に、答えは「依存する」です。
この記事への回答にリンクされた他の質問のいくつかの記事は、「実際の作業」を行わない「軽量」コンストラクタを提唱しています。ただし、コンストラクターで初期化して作業を開始することが適切な状況は数多くあります。
通常、インスタンス化が安価であり、インスタンス化が本質的に1つの概念的な成功の結果をもたらし、構築と仮説の開始の間に有効または意味のないことがある場合に適しています。
まず、Start()メソッドの考え方を特徴付けましょう。多くの場合、代わりにInit()またはOpen()などと呼ばれます。ファイルを開いたり作成したり、ネットワークポートや接続を開いたり、データベースに接続したり、ハードウェアにアクセスしたり、新しいスレッドを開始したりすることなどが含まれます。オブジェクトを作成し、これらの操作を正しく開始するのに意味がある状況とは離れてる?
ファイルシステム上のファイルを扱う例を見てみましょう。コンストラクターで最小限の作業を行う必要があるという立場をとる場合、一般的な使用法は、ファイルライタークラスをインスタンス化し、後でOpen()などを呼び出し、その後、何らかの方法でデータの提供を開始します。オブジェクトを常に有効な状態にしたいので、インスタンス化時にファイルにアクセスできることを確認する必要があります。ファイルにアクセスできない場合は、構築時に例外をスローする必要があります。問題がなければ、後でOpen()を呼び出して作業を開始できます。しかし、待ってください、人生はそれほど単純ではありません。インスタンス化とOpen()の間でファイルにアクセスできなくなった可能性があるため、競合状態を作成しました。したがって、そのシナリオを処理するには、Open()を別のtry/catchで囲む必要があります。 1回の操作でインスタンス化して開くよりも優れているのはどれですか。このシナリオでは、追加のメソッドはコンストラクターで作業を行うよりも悪いと主張します。
パラダイムを使用すると、デバッグとテストが簡単な方法で、さらに複雑なインスタンス化手順を実行できます。依存関係の注入は、多くの場合、そのための手段です。インスタンス化されたDatabaseConnectionが提供されているデータベースインターフェイスは、実際に目にした可能性のある一般的な例です。インスタンス化された有効なオブジェクトは、それらを使用して徐々にオブジェクトに注入され、途中で有効な状態が保証されます。
一部のコメントと回答では、wantを使用してオブジェクトをインスタンス化し、後でその処理を開始する場合があると述べています。これは、安価なオブジェクトにはほとんど意味がありません。変数を使用するときと同じくらい近くに変数を宣言することはかなり一般的に述べられていますが、なぜそのルールがオブジェクトと異なるのですか?そして、それらを別々に持っている場合、それらを別々に保つことによってあなた自身にどのような価値を提供していますか?ネットワークポートを開いている場合は、準備ができた時点で、必要なすべての情報が揃っています。そのため、追加のメソッドを呼び出すクラスの実際の違いは何ですか。
Start()をコンストラクターで実行することにより、クラスを使用する単一の方法があり、その単一の方法が正しい方法であり、コンパイラーがクラスを強制するようにすることができます。個別の開始メソッドがあるため、コンパイル時のプログラミングエラーとランタイムエラーをトレードオフする一時的なカップリングがあります。ある意味で、コンストラクターで開始すると、不変オブジェクトの概念上の利点を享受できますが、オブジェクトを実行するオブジェクトにとってはそうです。
今、私が言ったように、時々答えはイエスです、時にはノーです。 doesが公開されたStart()メソッドを持つことは意味がある場合があります。 RAIIとOOPおよび類似のパラダイムが存在せず、例外などがないため、選択の余地がない他のシステムとインターフェースしている場合があります。インスタンス化は実際には高価です。これらのケースやその他のケースでは、個別の開始メソッドを使用することが理にかなっています。
それはコンストラクターの目的ではないためです。コンストラクタは構築用です。オブジェクトを構築するときに、オブジェクトを使用する準備以外のことを期待したり、望んだりしません。彼らが何かをし始めたら、私は彼らに頼まなかった、あなたは私を驚かせます、オブジェクトユーザー。
最初に何かを実行できない限り、存在を拒否するオブジェクトを処理しないと、使用を構築から分離することは十分に困難です。コンストラクターは、オブジェクトを使用可能な状態にする必要があります。それで全部です。もう散らかっています。
Miskoは コンストラクターで作業する を避けるべきだと言っているためです。
あなたは私にあなたのように考えることを強いています
あなたは未来を言うことができません。私にはわからないので、私はこれを知っています。オブジェクトはどのように使用されますか?毎回そのように使用されると思いますか?多分誰かが後であなたが想像していなかった道の後でそれの使用法を見つけますが、実用的です。また、start
メソッドを呼び出すしないも必要です。そのロジックをコンストラクターに移動したので、 それらは運が悪い であり、コードの再利用の機会は失われます。コンストラクタでstart
メソッドを呼び出したいからです。
オブジェクトの新しいインスタンスを信頼できますか?
ソフトウェア開発における唯一の定数は、物事が変化することです。あなたのオブジェクトの新しいインスタンスが欲しいのですが、今すぐ欲しいです。ただし、コンストラクタでstartメソッドを呼び出します。次に、誰かがやって来て、startメソッドにいくつかの機能を追加しますが、-新しい依存関係は導入されていませんであるにもかかわらず、私が欲しかったインスタンスは完全に異なります。私はコードを何も変更しませんでしたが、今は機能せず、 それはすべてあなたの責任です です。コンストラクタでstart
メソッドを呼び出したいからです。
私はプログラマーであり、それは私が怠惰であることを意味します
今日は28の仕事をしています。私は午後2時の会議に行かなければならないことを知っています。会議室は角を曲がって廊下にあるので、午後1時58分にデスクを離れれば時間どおりに到着すると思います。他の人がそこに到着するのを待つ間、私が先に離れた場合、親指をいじるだけでそこに座っていることはわかっています。これは非効率的です。これが私の怠惰の主な理由です。 1:52 pmと1:58 pmの間にprogrammers.stackexchange.comを閲覧しているだけでも、それはmuch会議室に座って天井タイルを数えるよりも時間を有効に活用できます。また、午後1時57分に会議がキャンセルされたというメールが届く場合がありますが、午後1時55分にデスクを離れると、メールが届かず、おそらく午後2時5分頃になるでしょう。私が1人だけだったのではないかと少し心配しました。つまり、私は熱心なりたいと思ったので、実際に余分な10全体分カウントの上限を無駄にしていますタイル。それがまさに前回起こったことです。それらはそれほど多くないので、私が正しいことを確認するために、私はそれらを2回カウントしました(私はそうでした)。
これのポイントは、私は今あなたのオブジェクトの新しいインスタンスを必要としていますが、あなたがちょうど 私に気をつけさせてください が私が良くて準備ができていると確信しているときだけ、それを望んでいるということです実際に始めて欲しいと思います。コンストラクターでstart
を自動的に呼び出す場合は、-yoに希望する特定の時点でそのオブジェクトを構築するように強制しています。なんらかの理由でもっと早く構築したいのですが、その時点では、start
の準備ができていません。コンストラクタでstart
メソッドを呼び出したかったので、できません。
テスト
これが最大の理由だと思います。テストするオブジェクトをモックする必要がありますが、そのオブジェクトの内部は構築フェーズ中にmodified(作業が完了)です。ただし、モックオブジェクトだけでは、そのコードは実行される可能性がないため、それを使用する何かをテストする必要があるときはいつでもそのオブジェクトをモックするだけでなく、start
メソッドと同じ方法でモックオブジェクトを変更する必要があります毎回。ああ、ジョーはstart
メソッドを更新したので、ビルドが成功する前に 更新する必要のある57のテストがあります を意味します。これは厄介であり、おそらく、重いコンストラクターを持つオブジェクトに依存するものに対してテストが書き込まれることはありません。
yoはコンストラクタでstart
メソッドを呼び出す必要があるためです。
TL; DR
モバイルアプリでは、特定のオブジェクトは1回作成されますが、複数回開始されるため、コードの配置は慎重に行う必要があります悪いUXにつながるさまざまな不要な影響を避けてください。
モバイル開発の経験があるため、日常的に遭遇します1 特定のオブジェクトが実際に使用可能になる前に、一連の初期化またはlifecycleメソッドを実行する必要がある場合。
初めてアプリを作成する人にとっては、onCreate()
をonStart()
から、おそらくonResume()
から分離する必要はまったくないように思えるかもしれません。どうしてこれなの?想定されるユースケースは、デスクトップ上の電卓のようなものであるためでしょう-「アプリを開いて、必要なことを行い、大きな終了ボタンをクリックし、必要に応じて後でアプリを再起動してください」。確かに、これはいくつかのケースでうまくいくことができますが、アプリでの作業中に電話をかける必要がある場合、または写真をアップロードしようとするとどうなるでしょうか?
初心者がすべてをonCreate()
に入れることにした場合、アプリが再開されると、応答しなくなったり、予期しない状態(アニメーションが一時停止した?)になる可能性があります。すべてをonResume()
に入れると、オブジェクトを複数回インスタンス化することになるため、メモリリークが発生する可能性があります。
リソースが比較的限られているプラットフォームでは、すべての選択肢がユーザーエクスペリエンスに結びつくため、開発者はそれらをいつどのように使用するかに非常に注意する必要があります-アプリは応答性がありますか?電力を大量に消費しますか?ユーザーは同じアクションを何度も繰り返す必要がありますか?
結論として:初期化コードをいくつかのステージ(またはメソッド)に分割すると、パフォーマンス(または少なくともの外観のパフォーマンスが向上します。コードを不必要な回数実行する必要がないためです。 )および全体的なユーザーエクスペリエンス。
1 -この最も明確な例は、Android Activity
要約 またはiOS UIViewController
。