作成中は変更可能で、その後は変更できないメンバーを持つクラス


22

オブジェクトのコレクションを作成するアルゴリズムがあります。これらのオブジェクトは非常にわずかなものから開始されるため、作成中は変更可能ですが、アルゴリズム内のさまざまな場所にデータが入力されます。

アルゴリズムが完了した後、オブジェクトは決して変更されるべきではありませんが、ソフトウェアの他の部分によって消費されます。

これらのシナリオでは、以下に説明するように、クラスの2つのバージョンを使用することをお勧めしますか?

  • 可変のものはアルゴリズムによって作成され、その後
  • アルゴリズムが完了すると、データは不変オブジェクトにコピーされ、返されます。

3
質問を編集して、問題/質問を明確にしてください。
サイモンベルゴット

この質問にも興味があるかもしれません:.NETで
アイスキャンディー

回答:


46

おそらくビルダーパターンを使用できます。必要なデータを収集する目的で別の「ビルダー」オブジェクトを使用し、すべてのデータが収集されると、実際のオブジェクトを作成します。作成されたオブジェクトは不変にすることができます。


あなたのソリューションは私の要件に合ったものでした(すべてが記載されているわけではありません)。調査の結果、返されたオブジェクトはすべて同じ特性を持っているわけではありません。ビルダークラスには多くのNULL入力可能フィールドがあり、データが構築されると、3つの異なるクラスのいずれを生成するかを選択します。これらは継承によって互いに関連付けられます。
ポールリチャーズ

2
@Paulその場合、この回答で問題が解決した場合は、承認済みとしてマークする必要があります。
ライキン

24

これを実現する簡単な方法は、プロパティを読み取り、読み取り専用メソッドのみを呼び出すことができるインターフェイスと、そのクラスを作成できるインターフェイスを実装するクラスを用意することです。

それを作成し、前者を処理してから、後者を返し、対話する読み取り専用インターフェイスのみを提供するメソッド。これはコピーを必要とせず、作成者ではなく発信者が使用できるようにする動作を簡単に微調整できます。

次の例をご覧ください。

public interface IPerson 
{
    public String FirstName 
    {
        get;
    }

    public String LastName 
    {
        get;
    }
} 

public class PersonImpl : IPerson 
{
    private String firstName, lastName;

    public String FirstName 
    {
        get { return firstName; }
        set { firstName = value; }
    }

    public String LastName 
    {
        get { return lastName; }
        set { lastName = value; }
    }
}

class Factory 
{
    public IPerson MakePerson() 
    {
        PersonImpl person = new PersonImpl();
        person.FirstName = 'Joe';
        person.LastName = 'Schmoe';
        return person;
    }
}

このアプローチの唯一の欠点は、実装クラスに単純にキャストできることです。セキュリティの問題であれば、単にこのアプローチを使用するだけでは不十分です。これを回避するには、ファサードクラスを作成して可変クラスをラップします。これは、呼び出し元が動作し、内部オブジェクトにアクセスできないインターフェイスを単に表示するだけです。

このように、キャストすら役に立たないでしょう。両方とも同じ読み取り専用インターフェイスから派生できますが、返されたオブジェクトをキャストすると、Facadeクラスのみが得られます。Facadeクラスは、ラップされた可変クラスの基本状態を変更しないため、不変です。

これは、不変オブジェクトがコンストラクターを介して一度だけ構築されるという典型的な傾向に従わないことに言及する価値があります。当然のことながら、多くのパラメーターを処理する必要があるかもしれませんが、これらすべてのパラメーターを事前に定義する必要があるのか​​、それとも後で導入できるのかを自問する必要があります。その場合、必要なパラメーターのみを持つ単純なコンストラクターを使用する必要があります。つまり、プログラムの別の問題をカバーしている場合は、このパターンを使用しないでください。


1
オブジェクトを取得するコードはリフレクションを使用してオブジェクトを変更できるため、「読み取り専用」オブジェクトを返すことでセキュリティは向上しません。文字列でさえ、リフレクションを使用して変更できます(コピーされず、インプレースで変更されます)。
MTilsted

セキュリティの問題はプライベートクラスできれいに解決できます
エスベンスコフペダーセン

@Esben:MS07-052:コード実行結果はコード実行になります。コードはコードと同じセキュリティコンテキストで実行されているため、デバッガを接続するだけで何でもできます。
ケビン

Kevin1あなたはすべてのカプセル化についてそれを言うことができます。私は反射から保護しようとはしていません。
エスベンスコフペダーセン

1
「セキュリティ」という言葉を使用する際の問題は、すぐに誰かが私がより安全なオプションであると言うことは、最大のセキュリティとベストプラクティスに等しいと仮定することです。私も言ったことはありません。誰かが使用するためにライブラリを手渡している場合、難読化されていない場合(および難読化されている場合もあります)、セキュリティの保証を忘れることがあります。ただし、リフレクションを使用して返されたファサードオブジェクトを改ざんして、その中に含まれる内部オブジェクトを取得する場合、使用するライブラリを正確に使用していないという事実に全員が同意できると思います。
ニール

8

@JacquesBが言うようにBuilderパターンを使用することもできますし、代わりに、作成中にこれらのオブジェクトが変更可能でなければならないのは実際にはなぜだと思いますか?

言い換えると、必要な値をすべてコンストラクターに渡してインスタンスを一度に作成するのではなく、それらを作成するプロセスを時間内に分散する必要があるのはなぜですか?

Builderは、間違った問題の良い解決策になる可能性があるためです。

問題が10個のパラメーターのようなコンストラクターになり、オブジェクトを少しずつ構築して軽減したい場合、デザインが混乱していることを示している可能性があり、これらの10個の値は「袋に入れた」/いくつかのオブジェクトにグループ化...またはメインオブジェクトをいくつかの小さなオブジェクトに分割...

その場合-常に不変性に固執し、設計を改善するだけです。


コードがデータを取得するために複数のデータベースクエリを実行するため、一度にすべてを作成することは困難です。異なるテーブルと異なるデータベースのデータを比較します。
ポールリチャーズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.