コンストラクタなしで生きることはできますか?


25

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

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

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


どのプログラミング言語?
ブヨ

2
PHP(なぜ重要なのですか?)
アクセルフォーリー

8
Factoryパターンを使用すると、やりたいことが実現できるように見えます。
superM

1
注:@superMで説明したファクトリーパターンは、Inversion of Control(Dependency Injection)を実装する1つの方法です。
ヨアヒムザウアー

それは、おおよそJavaScriptがオブジェクトで行うことではありませんか?
サイザー14年

回答:


44

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

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

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

おそらくまだすべてを実装することができます(結局、まだ完全にチューリングしています)、いくつかのことを行うのがはるかに難しくなります。

個人的には、依存性の注入 / 制御の逆転を調べることをお勧めします。これらの手法では、具体的な実装クラスを切り替えることもできますが、コンストラクターの作成/使用を妨げることはありません。


「または「壊れた」オブジェクトのコンストラクターを否定することを明白にする」とはどういう意味ですか?
オタク

2
@Geek:コンストラクターは引数を検査し、それらが作業オブジェクトになるかどうかを判断できます(たとえば、オブジェクトが必要なHttpClient場合、そのパラメーターがnullでないかどうかをチェックします)。そして、これらの制約が満たされない場合、例外をスローする可能性があります。それは、construct-and-set-valuesアプローチでは実際には不可能です。
ヨアヒムザウアー

1
OPは内部のコンストラクターの外部化を記述しているだけだと思いますがinit()、これは完全に可能です。
ピーター

26

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

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

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

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

カプセル化

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

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

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


15

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

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

ただし、独自のコンストラクタを厳密に「必要」にすることはありません。もちろん、クラスやオブジェクトも厳密に「必要」ではありません。

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


絶対に。そもそもクラスやオブジェクトがなくても生きることができます。
JensG 14年

5
すべてが強力なアセンブラーを称えます!
DavorŽdralo

9

コンストラクターを使用する利点は、無効なオブジェクトが決してないことを保証するのが容易になることです。

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

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

回避策は、呼び出し元に返す前に作成するすべてのオブジェクトの有効性をチェックするファクトリーメソッドを介してのみオブジェクトを作成することです。


3

$ 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存在しませんが、質問で説明されているシステムのように初期化されません。オブジェクトが常に正しく使用されることを保証できないため、このような中間状態を回避することをお勧めします。


2

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

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

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

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

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

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

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


2

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

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

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;
    }
}

以前にデザインパターンを使用する代わりにJoachim Sauerが推奨したとおり、Factoryについて読んでくださいDependency InjectionMark Seemannによる.NETのDependency Injectionを読むことをお勧めします


1

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

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

* 私は、テストが最初にタイプを考慮し、次に達成したい結果を考慮しなければならないという事実に言及しています。次に、大規模なネストされたテストを作成します。


1

他のいくつかの答えのバランスをとるために、以下を主張します:

コンストラクターを使用すると、オブジェクトのすべてのメンバー変数を有効な状態に設定できます。無効なオブジェクトが存在することはないため、多くのバグを回避できます。

そして

コンストラクターでは、そのようなクラスの無効なインスタンスが存在しないように、オブジェクトは独自の構築ロジックを所有します。

このようなステートメントは、次の前提を意味する場合があります。

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

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


0

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

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

これには、メンタルな設計プロセスを早めに配線しないという利点もあります。あなたがしているのはせいぜい先の作業のための基本的なセットアップだけなので、決して使用されることはないかもしれないロジックを初期化または実行しません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.