5

Java Bean に不変性を提供する可能性について非常に興味があります (ここでの Bean とは、メンバーにゲッターとセッターを提供する空のコンストラクターを持つクラスを意味します)。明らかに、これらのクラスは不変ではなく、データ層から値を転送するために使用される場所は、実際の問題のようです。

この問題に対する 1 つのアプローチは、StackOverflow で言及されている「C# の不変オブジェクト パターン」と呼ばれ、オブジェクトは完全にビルドされると凍結されます。私には別のアプローチがあり、それについて人々の意見を聞きたいと思っています。

パターンには Immutable と Mutable の 2 つのクラスが含まれ、Mutable と Immutable は両方とも、非変更 Bean メソッドを提供するインターフェースを実装します。

例えば

public interface DateBean {
    public Date getDate();
    public DateBean getImmutableInstance();
    public DateBean getMutableInstance();
}

public class ImmutableDate implements DateBean {
    private Date date;

ImmutableDate(Date date) {
    this.date = new Date(date.getTime());
}

public Date getDate() {
    return new Date(date.getTime());
}

    public DateBean getImmutableInstance() {
        return this;
    }

    public DateBean getMutableInstance() {
        MutableDate dateBean = new MutableDate();
        dateBean.setDate(getDate());
        return dateBean;
    }
}

public class MutableDate implements DateBean {
    private Date date;

public Date getDate() {
    return date;
}

public void setDate(Date date) {
    this.date = date;
}

public DateBean getImmutableInstance() {
    return new ImmutableDate(this.date);
}

    public DateBean getMutableInstance() {
        MutableDate dateBean = new MutableDate();
        dateBean.setDate(getDate());
        return dateBean;
    }
}

このアプローチにより、リフレクションを使用して (通常の慣例により) Bean を構築することができ、また、最も近い機会に不変のバリアントに変換することもできます。残念ながら、Bean ごとに明らかに大量のボイラープレートがあります。

この問題に対する他の人々のアプローチを聞くことに非常に興味があります。(良い質問を提供していないことをお詫びします。議論するのではなく、答えることができます:)

4

3 に答える 3

3

いくつかのコメント (必ずしも問題ではありません):

  1. Date クラスはそれ自体が可変であるため、不変性を保護するために正しくコピーしていますが、個人的には、コンストラクターで long に変換し、getter で新しい Date(longValue) を返すことを好みます。
  2. 両方の getWhateverInstance() メソッドがキャストを必要とする DateBean を返します。代わりに特定の型を返すようにインターフェイスを変更することをお勧めします。
  3. 必要に応じて、共通の (つまり、get のみの) インターフェイスを共有して、1 つは可変でもう 1 つは不変の 2 つのクラスを持ちたいと思います。前後に多くの変換があると思われる場合は、コピー コンストラクターを両方のクラスに追加します。
  4. 私は不変クラスがフィールドをfinalとして宣言して、コンパイラーに不変性も強制させることを好みます。

例えば

public interface DateBean {
    public Date getDate();
}

public class ImmutableDate implements DateBean {
    private final long date;

    ImmutableDate(long date) {
       this.date = date;
    }

    ImmutableDate(Date date) {
       this(date.getTime());
    }

    ImmutableDate(DateBean bean) {
       this(bean.getDate());
    }

    public Date getDate() {
         return new Date(date);
    }
}


public class MutableDate implements DateBean {
    private long date;

    MutableDate() {}

    MutableDate(long date) {
       this.date = date;
    }

    MutableDate(Date date) {
       this(date.getTime());
    }

    MutableDate(DateBean bean) {
       this(bean.getDate());
    }

    public Date getDate() {
        return new Date(date);
    }

    public void setDate(Date date) {
        this.date = date.getTime();
    }

}
于 2008-11-20T23:27:57.827 に答える
2

委任パターンを使用すると思います-コンストラクターで指定する必要がある単一の DateBean メンバーを持つ ImmutableDate クラスを作成します。

public class ImmutableDate implements DateBean
{
   private DateBean delegate;

   public ImmutableDate(DateBean d)
   {
      this.delegate = d;
   }

   public Date getDate()
   {
      return delegate.getDate();
   }
}

DateBean d に不変性を強制する必要がある場合は、新しい ImmutableDate(d) を追加します。私は賢明で、デリゲートを委任しないようにすることもできましたが、おわかりいただけたでしょうか。これにより、クライアントがそれを変更可能なものにキャストしようとする問題が回避されます。これは、JDK が Collections.unmodifiableMap() などで行うのとよく似ています (ただし、これらの場合でも、ミューテーション関数を実装する必要があり、実行時例外をスローするようにコード化されています。ミューテーター)。

これも退屈なボイラープレート コードですが、Eclipse のような優れた IDE では、マウスを数回クリックするだけで自動生成できるようなものです。

多くのドメイン オブジェクトに対してこのようなことを行う場合は、動的プロキシまたは AOP の使用を検討することをお勧めします。その場合、任意のオブジェクトのプロキシを構築し、すべての get メソッドを委譲し、必要に応じて set メソッドをトラップまたは無視するのは比較的簡単です。

于 2008-11-21T21:31:47.000 に答える
1

インターフェイスとキャストを使用して、Bean の可変性を制御します。getImmutableInstance()やのようなメソッドでドメイン オブジェクトを複雑にする正当な理由がわかりませんgetMutableInstance()

継承と抽象化を利用しないのはなぜですか? 例えば

public interface User{

  long getId();

  String getName();

  int getAge();

}

public interface MutableUser extends User{

  void setName(String name);

  void setAge(int age);

}

コードのクライアントが行うことは次のとおりです。

public void validateUser(User user){
  if(user.getName() == null) ...
}

public void updateUserAge(MutableUser user, int age){
  user.setAge(age);
}

それはあなたの質問に答えますか?

yc

于 2008-11-20T23:29:38.867 に答える