3

私はクラスを持っています:スケジュール。

public class Schedule {

private int locationNum;
private int cost;
private String costReason; 
private Date weekOfChange;
private Date dayOfChange;
private String changeReason; 

// and all those getters and setters

public Schedule(int locationNum, int cost, String costReason, Date weekOfChange, Date dayOfChange, String changeReason) throws ApplicationException {
//change is all or nothing - all attributes are present or none
if((weekOfChange!=null && dayOfChange!=null && changeReason!=null) || (weekOfChange==null  && dayOfChange == null && changeReason == null))  {
this.weekOfChange = weekOfChange;
this.dayOfChange = dayOfChange;
this.changeReason = changeReason;
}
else { throw new ApplicationException();}
//similary another if block to ensure that if cost is specified 
//then there exists the corresponding reason code for it.
}
}

これまでのところ、スケジュールクラスが気に入っています。ただし、チェックは完了していません。他のチェックを行う必要があります。

  • locationNumは、データベース内の有効なストア番号です。
  • changeReasonテキストは、データベース内の6つの異なるchangeReasonコードの1つです。
  • などなど...

通常、これらをScheduleクラスに書き込むことはありません。明らかに、このクラスからDAOを呼び出すことはできません。したがって、ビジネスレイヤーとある種のバリデータークラスがあり、Scheduleタイプのオブジェクトを受け入れ、一連のデータベース検証を順番に実行し、表示などのエラーを収集します。

さて、ここに私の質問があります:

  1. ScheduleをPOJOとして扱い、オブジェクトがそれ自体を検証する責任を負わないと主張する場合、コンストラクター内のすべてのコードをビジネスレイヤーのバリデータークラスに移動する必要があります。しかし、私がこれを行うとしたら、スケジュールは貧血ではありませんか?これは、彼らが単一責任の原則の違反と呼んでいるものですか?
  2. コンストラクターにあるコードをビジネスレイヤークラスに移動して、あらゆる種類の検証がビジネスレイヤーに含まれるようにしたとしましょう。データストアで誰かがdayOfChangeをNULLに変更し、データベースからオブジェクトを読み込んでいると仮定します。さて、この種のオブジェクトを使用すると、アプリケーションが破損する可能性がありますね。coz検証ルールが満たされていると仮定してコードを記述します。私の質問は混乱していると思いますが、私が言いたいのは、このコンテキストでは、Scheduleクラスによって隠されたデータの整合性を確保するために、コンストラクターでこれらのチェックを実行したいということです。
  3. それは通常どのように行われますか?ベストプラクティスは何ですか?

この議論へのあなたの関与に感謝します。

4

5 に答える 5

5

本当に「これらすべてのゲッターセッター」がある場合は、コンストラクターよりもクラスをより適切に検証する必要があります。クラスの不変条件が、すべてのweekOfChange、dayOfChange、およびchangeReasonがnullであるか、すべてがnullである必要がないようなものである場合、セッターはすぐにクラスを無効な状態にします。セッターがいるという意味ですか、それともクラスが不変であるという意味ですか?

検証を行う前に、クラスの分析を行う必要があります。与えられた状態のすべての有効な状態と不変量を見てください。次に、クラスを可変にするか不変にするかを理解します。相互に依存する共変量(weekOfChange、dayOfChannge、changeReasonなど)がある場合は、それらを独自のクラスにパッケージ化し、Scheduleクラスで構成を使用するのが理にかなっています。これにより、これらのフィールドに関する「ルール」が1つの場所に配置され、スケジュールが簡素化されます。 。

他の共同分野(コストやコスト理由など)でも同じです。次に、Scheduleは自己検証クラスで構成されます。これらの両方が不変であり、スケジュールが不変である場合、あなたは自分自身をはるかに簡単なドメインにします。

したがって、あなたの質問に答えるには、クラスがその状態と不変条件を定義し、実用的な場合は最小限のものだけを共同作業者に公開する方がよいでしょう。スケジュールの内部状態に対する責任はスケジュールにあり、もう少し設計を行うだけで比較的簡単に実行できます。

だからあなたは

    class Schedule {
         private Change change;
         private Cost cost;
         private Location location;

        public Schedule(Location location, Cost cost, Change change) {
           this.change = change;
           this.cost = cost;
           this.location = location;
        }
}

そして、のような協力者

public class Change {
    private Date weekOfChange; //shoudln't these two fields be one, a date is a date after all??
    private Date dayOfChange; //shoudln't these two fields be one??
    private String changeReason; 

    public Change(Date weekOfChange Date dayOfChange, String changeReason) {
      this.weekOfChange = weekOfChange;
      ...etc.
    } 
}

ただし、クライアントコードによって渡される可変値の防御的なコピーを使用して、クラスの不変条件を保護することを強くお勧めします。

于 2009-06-04T16:10:08.400 に答える
4

ScheduleをPOJOとして扱い、オブジェクトがそれ自体を検証する責任を負わないと主張する場合、コンストラクター内のすべてのコードをビジネスレイヤーのバリデータークラスに移動する必要があります。しかし、私がこれを行うとしたら、スケジュールは貧血ではありませんか?これは、彼らが単一責任の原則の違反と呼んでいるものですか?

POJOは、特定のクラスから派生したり、バイトコードエンハンサーを使用してORマッピングなどの目的の機能を取得したりする必要がないことを意味するだけです。オブジェクトが機能のない貧血であるべきだという意味ではありません。

さて、この種のオブジェクトを使用すると、アプリケーションが破損する可能性がありますね。coz検証ルールが満たされていると仮定してコードを記述します。私の質問は混乱していると思いますが、私が言いたいのは、このコンテキストでは、Scheduleクラスによって隠されたデータの整合性を確保するために、コンストラクターでこれらのチェックを実行したいということです。

Scheduleクラスのコンストラクターとは別のクラスで定義されたルールを呼び出すことができないと誰が言いますか?非公開フィールドにアクセスする必要がある場合は、それらをパッケージプライベートにして、同じパッケージにルールクラスを含めるか、パラメーターとしてルールに渡すことができます。

それは通常どのように行われますか?ベストプラクティスは何ですか?

検証ルールを別のクラスに移動するかどうかは、誤解されている流行語ではなく、アプリケーションのニーズに依存する必要があります。

個別のルールクラスを使用する理由は次のとおりです。

  • ルールは非常に多く、非常に複雑であるため、すべてをScheduleクラスに入れると、ルールが大きくなりすぎます。
  • すべてのルールを常に適用する必要はありません。
  • ルールを再利用して、必ずしも同じ継承階層にあるとは限らない(つまり、インターフェースを介して機能する)異なるクラスを操作できます。
于 2009-06-04T08:43:48.573 に答える
2

コンストラクターには2つの異なる動作があるようScheduleです。したがって、2つのコンストラクターが必要です。おそらく2つの実装クラスですら。

locationNumおよびchangeReason。これらは現在弱いタイプです。彼らはおそらく独自のクラスを持つべきです。あなたの質問に続いて、有効な値はコンテキストに依存します。現時点でScheduleは、コンテキストはありません。コンテキストから独立した状態を保つことができます。その場合、およびScheduleにデータベースの制約はありません。逆にすると、、はコンテキストに依存する可能性があり、コンストラクターはそれらがすべて同じコンテキストにあることを確認するだけです。パッケージプライベートコンストラクター(貧乏人として機能する)は、チェックを省略できます。locationNumchangeReasonSchedulelocationNumchangeReasonfriend

POJOは、(適切に記述された)オブジェクト、つまり動作を意味します。問題は、それを「構造体」を意味するために使用しているようです。構造体は避けてください。

ビジネスレイヤーは誤解を招くようです。それはビジネスエンティティのように見えます、それはビジネス行動を持っているべきです。ただし、ビジネスプロセス(サービス)が含まれていない場合があります。

アプリケーションは、(パブリック)スキーマを含むデータベースに関する特定のことを常に想定します。制約を削除すると、アプリケーションが破損する可能性があります。これは、依存しているコードを同様に変更した場合と同じです。

一般的な方法は、手続き型コードを使用して貧血のオブジェクトを作成することです。ほとんどの場合、これはベストプラクティスではありません。

于 2009-06-04T09:48:25.683 に答える
1

実際に別のクラスのインスタンス、たとえばLocationを参照しているのに、locationNumがintであるのはなぜですか?適切なオブジェクト参照を使用する場合は、スケジュールでそれらを検証し、参照整合性を適用するようにORM(存在する場合)を構成できます。

于 2009-06-04T08:38:28.617 に答える
0

あなたの質問に答えるために:

1)あなたのクラスは貧血になるとは思いません。今のところDTOになるでしょう、これで問題はないと思います。

2)検証がビジネスレイヤーにある場合、検証はまだ存在するため、無効なオブジェクトを取得することを意味するわけではありません。通常、検証プロセスは、InvalidDataExceptionなどについて不平を言い、許可しません。 「破損した」オブジェクトを使用します。nullの日付が許容値でない場合(nullは通常データベースに入ることができませんでした)、business / daoレイヤーはこのエラーをキャッチし、オブジェクトがアプリで使用されないようにする必要があります。

于 2009-06-04T09:33:27.397 に答える