たとえば、Player
とSeason
の両方のクラスがあるような、典型的な「シーズン」構造を持つ特定のスポーツまたはゲームに関する結果を保存するWebサイトを想像してみてください。特定のシーズン中のプレーヤーの平均スコアを取得するには、これを$player->averageScore($season)
で実行する必要がありますか、それとも$season->playerAverageScore($player)
で実行する必要がありますか?どうして?
これは、1人のプレーヤーの統計情報を計算するためにPlayerStatistics
クラスが必要であるように思われます。 SeasonStatistics
クラスを利用して、シーズン全体の統計を計算することもできます。私の頭の外では、統計はしばしば以下の事柄のために保持されます:
これにより、これらの計算がエンティティ自体(プレイヤー、シーズン)から効果的に切り離され、さまざまなレベルであらゆる種類の統計を計算するための多くの追加オプションが提供されます。
質問に対するダンクのコメントはこれを最もよくまとめており、将来これらの決定を行うための非常に良いガイドラインを提供すると思います:
...それが正当にどちらかの方向に進むことができる場合、それらを結びつけるクラスがありません...
あなたはそれより簡潔にすることはできません。
SeasonScoreCalculator
クラスとaverageScore
メソッドを使用して、シーズンとプレーヤーの両方を取得しないのはなぜですか?
重要なのは、メソッドがフェンスのどちら側に落ちるかを決定できない場合、それは1)クラスが過度に実行していること、2)単純化を可能にする概念が欠けていることの兆候である可能性があります。
これらのタイプの問題は、通常、「誰が誰を知る必要があるか」の分析で解決されます。
プレーヤーはシーズンとは何かを知っているべきだと私には思われますが、シーズンはプレーヤーが何であるかを知っているべきではありません。したがって、私は間違いなくplayer->averageScore(season)
アプローチを採用します。
この種類の推論は、現在のシナリオだけでなく、あらゆるシナリオで使用できます。
学習して適用するルールは次のとおりです。
「実世界のオブジェクトに対するアクションは、オブジェクト指向コードでそのオブジェクトのメソッドになります」
指向コード内のオブジェクトは、それ自体が方法を知っています。したがって、プレーヤーはそのスコアを知っており、それを反映する方法があります。
手続き型コードとは対照的に、プロシージャは他の何か(オブジェクト、別のプロシージャ、データなどの可能性があります)に情報を要求するので、プロシージャはどのように、どのように動作するかを決定できます。
ほとんどの場合、メソッドはPlayerクラスではなくSeasonに属していますが、どちらの方法でもかまいません。最も重要なことは、循環依存を防ぐことです。シーズンがプレーヤーのメソッドを呼び出す場合、プレーヤーはシーズンのメソッドを呼び出さないでください。およびその逆。あなたはどちらが他に依存するかを選ぶようになります。 Playerのメソッドを呼び出すSeasonのメソッドが既にある場合、選択肢はありません。PlayerのどのメソッドもSeasonのメソッドを呼び出すことができません。
グレッグバーガードが示唆するように、これが本当に統計目的である場合、DDDで一般化されている read-models を明示的に使用できると思います。これは [〜#〜] cqrs [〜#〜] 概念の明示です。ポイントは、データをクエリするために、コマンド側で使用され、ビジネスロジックを実装するオブジェクトを必要としないことです。したがって、あなたのケースでは、次のような別のリポジトリを持つことができます:
class StatisticsRepository
{
public function getByPlayerAndSeason(PlayerId $playerId, SeasonId $seasonId)
{
// db query
return new Statistics(
// here it goes raw data
);
}
}
生のデータを単純な配列として返すことも、コマンド側で使用されるリッチドメインオブジェクトとは関係のない読み取りモデルオブジェクトを使用することもできます。
いくつかの利益があります:
おそらく、getAverageScore(season, player)
として言及していないクラスにあるはずです。おそらく、シーズンとプレイヤーはすでにクラスのメンバーなので、単にgetAverageScore()
...で済む可能性があります。
これは、OOPの非常に古い質問です。パラダイムは、相互作用で1つのオブジェクトを他のオブジェクトよりも優先するためです。結局のところ、メソッド名の左側に配置できるのは1つのオブジェクトだけで、他のすべてのオブジェクトはその右側にあります(OO牛自体が牛乳を取り除くか、またはOO牛乳を取り去る必要がありますか?)
最初に尋ねる質問は、コラボレーションの結果としてどのオブジェクトが状態を変更する必要があるかということです。そのオブジェクトのクラスは、おそらくメソッドを収容するクラスです。
あなたはおそらくあなたの例では状態が変化していないと考えているでしょうが、それはおそらく間違っています。一部のコードでは、平均スコアが必要です。そのコードの一部は、情報を含むdoingを計画しているため、メソッドが存在する可能性が高い場所です。
経験則では、ポイントがどのように表されるかを基になるデータモデルを確認します。モデルでポイントが実際にどのように表現されるかを述べないため、特定のケースに誰も答えることができませんが、「ポイント」が「プレーヤー」と「ゲーム」への参照を持つエンティティであるモデルを想像すると"(次に"シーズン "への参照が含まれます)、当然、ポイントクラスがあり、これにより、プレーヤーとシーズンのポイントを集計できます。