web-dev-qa-db-ja.com

コンストラクターなしで生活できますか?

何らかの理由ですべてのオブジェクトがこの方法で作成されたとしましょう$ obj = CLASS :: getInstance()。次に、セッターを使用して依存関係を注入し、$ obj-> initInstance()を使用して初期化を開始します。コンストラクタをまったく使用しない場合、解決できない実際のトラブルや状況はありますか?

追伸この方法でオブジェクトを作成する理由は、いくつかのルールに従ってgetInstance()内のクラスを置き換えることができるためです。

私はPHPで働いています、それが問題なら

25
Axel Foley

これはあなたのデザインスペースを著しく妨げると思います。

コンストラクターは、渡されたパラメーターを初期化および検証するのに最適な場所です。そのためにそれらを使用できなくなった場合、初期化、状態処理(または「壊れた」オブジェクトのコンストラクターの拒否)は非常に困難になり、部分的に不可能になります。

たとえば、すべてのFooオブジェクトがFrobnicatorを必要とする場合、Frobnicatorがnullでないかどうかをコンストラクタでチェックインする場合があります。コンストラクタを削除すると、チェックが難しくなります。使用されるすべての場所で確認しますか? init()メソッド内(コンストラクターメソッドを効果的に外部化する)?決してそれをチェックせず、最高のものを望んでいますか?

あなたはできますはまだすべてを実装していますが(結局のところ、まだ完全に進んでいます)、いくつかのことはかなり難しくなります。

個人的には、私は Dependency Injection / Inversion of Control を調べることをお勧めします。これらの手法では、具象実装クラスを切り替えることもできますが、コンストラクターの作成/使用を妨げるものではありません。

44
Joachim Sauer

コンストラクターの2つの利点:

コンストラクターを使用すると、オブジェクトの構築手順をアトミックに実行できます。

コンストラクターを避けてすべてに対してセッターを使用することはできますが、Joachim Sauerが提案した必須プロパティについてはどうですか?コンストラクターを使用すると、オブジェクトは独自の構築ロジックを所有し、このようなクラスの無効なインスタンスがないを確実にします。

Fooのインスタンスを作成するために3つのプロパティを設定する必要がある場合、コンストラクターは3つすべてへの参照を取得し、それらを検証して、無効な場合は例外をスローできます。

カプセル化

セッターのみに依存することで、オブジェクトを適切に構築する負担がオブジェクトの消費者にあります。有効なプロパティのさまざまな組み合わせが存在する可能性があります。

たとえば、すべてのFooインスタンスには、プロパティBarとしてのbarのインスタンス、またはプロパティBarFinderとしてのbarFinderのインスタンスのいずれかが必要です。どちらでも使用できます。すべての有効なパラメーターのセットに対してコンストラクターを作成し、その方法で規則を適用できます。

オブジェクトのロジックとセマンティクスは、オブジェクト自体の内部にあります。それは良いカプセル化です。

26
Brandon

はい、あなたはコンストラクターなしで生きることができます。

確かに、大量のボイラープレートコードが重複することになります。また、アプリケーションの規模が大きい場合、ボイラープレートコードがアプリケーション全体で一貫して使用されていない場合、問題の原因を特定するために多くの時間を費やすことになります。

しかし、いいえ、あなた自身のコンストラクタを厳密に「必要」とするわけではありません。もちろん、クラスやオブジェクトを厳密に「必要」としているわけでもありません。

オブジェクトの作成に何らかのファクトリパターンを使用することが目標である場合、オブジェクトの初期化時にコンストラクタを使用することは相互に排他的ではありません。

15
GrandmasterB

コンストラクタを使用する利点は、無効なオブジェクトが発生しないようにすることを容易にすることです。

コンストラクターを使用すると、オブジェクトのすべてのメンバー変数を有効な状態に設定できます。次に、ミューテーターメソッドがオブジェクトを無効な状態に変更できないことを確認すると、無効なオブジェクトがなくなるため、多くのバグを回避できます。

しかし、新しいオブジェクトが無効な状態で作成され、いくつかのセッターを呼び出してそれを使用できる有効な状態にする必要がある場合、クラスのコンシューマーがこれらのセッターの呼び出しを忘れたり、誤って呼び出したりする危険があります。無効なオブジェクトが作成されます。

回避策としては、作成者が作成したすべてのオブジェクトの有効性をチェックしてから呼び出し元に返すファクトリメソッドを通じてのみオブジェクトを作成します。

9
Philipp

$ obj = CLASS :: getInstance()。次に、セッターを使用して依存関係を注入し、$ obj-> initInstance()を使用して初期化を開始します。

あなたはこれを必要以上に難しくしていると思います。コンストラクタを介して依存関係を問題なく注入できます。依存関係がたくさんある場合は、辞書のような構造を使用して、使用するものを指定できます。

$obj = new CLASS(array(
    'Frobnicator' => (),
    'Foonicator' => (),
));

そして、コンストラクター内では、次のように一貫性を確保できます。

if (!array_key_exists('Frobnicator', $args)) {
    throw new Exception('Frobnicator required');
}
if (!array_key_exists('Foonicator', $args)) {
    $args['Foonicator'] = new DefaultFoonicator();
}

$argsを使用して、必要に応じてプライベートメンバーを設定できます。

このように完全にコンストラクター内で行われると、質問で説明されているシステムのように、$objが存在するが初期化されていない中間状態はありません。オブジェクトが常に正しく使用されることを保証できないため、このような中間状態を回避することをお勧めします。

3
Izkata

私は実際に同じようなことを考えていました。

私が尋ねた質問は、「コンストラクターは何をするのか、そしてそれを別の方法で行うことは可能ですか?」でした。私はそれらの結論に達しました:

  • 一部のプロパティが確実に初期化されます。それらをパラメーターとして受け入れ、設定する。しかし、これはコンパイラーによって簡単に強制できます。フィールドまたはプロパティに「必須」の注釈を付けるだけで、コンパイラはインスタンスの作成中にすべてが正しく設定されているかどうかをチェックします。インスタンスを作成する呼び出しはおそらく同じで、コンストラクターメソッドはありません。

  • プロパティが有効であることを確認します。これは、アサート条件によって簡単に実現できます。ここでも、プロパティに正しい条件で注釈を付けるだけです。一部の言語はすでにこれを行っています。

  • より複雑な構築ロジック。最近のパターンでは、コンストラクターでこれを行うことはお勧めしませんが、専用のファクトリーメソッドまたはクラスの使用を提案しています。したがって、この場合のコンストラクターの使用は最小限です。

だからあなたの質問に答えるために:はい、それは可能だと思います。しかし、言語設計にいくつかの大きな変更が必要になります。

そして、私は私の答えがかなりOTであることに気づきました。

2
Euphoric

はい、コンストラクタを使用しなくてもほぼすべてを実行できますが、これは明らかにオブジェクト指向プログラミング言語の利点を浪費しています。

現代の言語(ここでプログラミングするC#について説明します)では、コンストラクターで実行できるコードの一部を制限できますonly。不器用な間違いを回避できるおかげで。そのようなものの1つはreadonly修飾子です:

public class A {
    readonly string rostring;

    public A(string arg) {
        rostring = arg;
    }

    public static A CreateInstance(string arg) {
        var result = new A();
        A.rostring = arg;  // < because of this the code won't compile!
        return result;
    }
}

以前はFactoryを使用する代わりに Joachim Sauerが推奨 を使用する代わりに、Dependency Injectionの設計パターンを読みます。 Mark Seemannによる.NETでの依存性注入 を読むことをお勧めします。

2
SOReader

絶対に可能な要件に応じて、型付きのオブジェクトをインスタンス化します。システム自体のグローバル変数を使用して特定のタイプを返すこと自体がオブジェクトである可能性があります。

ただし、「すべて」になる可能性があるコード内のクラスは動的タイプの概念です。私は個人的に、このアプローチはコードに矛盾を生じ、テストを複雑にし*、提案された作業の流れに関して「未来は不確実になる」と信じています。

* 私はテストが最初に型を検討し、次に達成したい結果を検討する必要があるという事実を参照しています。次に、大きなネストされたテストを作成しています。

1
marcocs

他の答えのいくつかのバランスを取るために、次のように主張します:

コンストラクターは、オブジェクトのすべてのメンバー変数を有効な状態に設定する機会を与えます...無効なオブジェクトが存在することはなく、多くのバグからあなたを救います。

そして

コンストラクターを使用すると、オブジェクトは独自の構築ロジックを所有し、そのようなクラスの無効なインスタンスがないことを保証します。

このような記述は、次の仮定を示唆することがあります。

クラスに、終了するまでにオブジェクトを有効な状態にしたコンストラクターがあり、クラスのどのメソッドもその状態を変更して無効にしない場合、クラス外のコードでオブジェクトを検出することはできません。無効な状態のそのクラスの。

しかし、これはまったく真実ではありません。ほとんどの言語には、this(またはselfまたは言語がそれを呼び出すものは何でも)を外部コードに渡すコンストラクターに対するルールがありません。このようなコンストラクターは、上記のルールを完全に遵守しますが、半構築オブジェクトを外部コードに公開するリスクがあります。それはマイナーなポイントですが、簡単に見落とされます。

1

これは少し逸話ですが、私は通常、オブジェクトが完全で使用可能になるために不可欠な状態のコンストラクタを予約します。 setter-injectionがなければ、コンストラクターが実行されると、オブジェクトはそれに必要なタスクを実行できるはずです。

据え置きできるものは何でも、私はコンストラクターから外します(出力値の準備など)。このアプローチでは、何にもコンストラクタを使用しないように感じますが、依存性注入は理にかなっています。

これには、メンタルデザインプロセスをハードワイヤリングして、時期尚早に何もしないという追加の利点もあります。せいぜいあなたがしているのは先の作業のための基本的なセットアップだけなので、決して使用されない可能性のあるロジックを初期化したり実行したりすることはありません。

0
Omega