オブジェクトを保存して取得したい場合、それを処理する別のクラスを作成する必要がありますか、それともクラス自体で行う方が良いでしょうか?それとも両方を混ぜるか?
OODパラダイムによればどちらが推奨されますか?
例えば
Class Student
{
public string Name {set; get;}
....
public bool Save()
{
SqlConnection con = ...
// Save the class in the db
}
public bool Retrieve()
{
// search the db for the student and fill the attributes
}
public List<Student> RetrieveAllStudents()
{
// this is such a method I have most problem with it
// that an object returns an array of objects of its own class!
}
}
対。 (私は以下が推奨されることを知っていますが、Student
クラスのまとまりに少し反対のようです)
Class Student { /* */ }
Class DB {
public bool AddStudent(Student s)
{
}
public Student RetrieveStudent(Criteria)
{
}
public List<Student> RetrieveAllStudents()
{
}
}
それらを混ぜてみませんか?
Class Student
{
public string Name {set; get;}
....
public bool Save()
{
/// do some business logic!
db.AddStudent(this);
}
public bool Retrieve()
{
// build the criteria
db.RetrieveStudent(criteria);
// fill the attributes
}
}
単一責任の原則、懸念の分離および機能的結合。これらの概念について読んだ場合、得られる答えはそれらを分離するです。
Student
を「DB」クラス(またはStudentRepository
、より一般的な規則に従う)から分離する簡単な理由は、永続化を担当するコードに影響を与えずに、Student
クラスに存在する「ビジネスルール」を変更できるようにするためです。およびその逆。
この種の分離は、ビジネスルールと永続性の間だけでなく、システムの多くの懸念の間でも非常に重要であり、無関係なモジュールへの影響を最小限に抑えて変更を加えることができます(場合によっては避けられないため最小限に抑えます)。これは、より堅牢なシステムを構築するのに役立ちます。これは、保守が容易であり、絶え間ない変更がある場合により信頼性が高くなります。
最初の例のように単一のクラスを使用するか、DB
の依存関係としてStudent
を使用して、ビジネスルールと永続性を混在させることにより、2つの非常に異なる懸念を結合します。彼らは一緒に属しているように見えるかもしれません。同じデータを使用しているため、まとまりがあるようです。しかし、これが問題です。凝集度は、プロシージャ間で共有されるデータだけで測定することはできません。それらが存在する抽象化のレベルも考慮する必要があります。実際、理想的な結束のタイプは次のように説明されます。
機能的凝集度 は、モジュールの一部がグループ化されている場合です。これらはすべて、モジュールの1つの明確に定義されたタスクに寄与するためです。
そして明らかに、Student
についての検証を実行しながらそれを永続化しても、「単一の明確に定義されたタスク」を形成しません。繰り返しになりますが、ビジネスルールと永続化メカニズムはシステムの2つの非常に異なる側面であり、優れたオブジェクト指向設計の多くの原則により、これらは別々に保つ必要があります。
Clean Architecture を読んで を読むことをお勧めしますこれは単一責任の原則 (非常によく似た例が使用されています)について話し、見て これもクリーンアーキテクチャ についての話です。これらの概念は、そのような分離の背後にある理由を概説しています。
どちらのアプローチも単一責任原則に違反しています。最初のバージョンでは、Student
クラスに多くの責任を与え、それを特定のDBアクセステクノロジーに関連付けます。 2つ目は、巨大なDB
クラスにつながります。これは、学生だけでなく、プログラム内の他の種類のデータオブジェクトにも責任があります。編集:DBクラスとStudent
クラスの間に循環依存関係を作成するため、3番目のアプローチは最悪です。
したがって、おもちゃのプログラムを作成しない場合は、それらを使用しないでください。代わりに、StudentRepository
のような別のクラスを使用して、自分でCRUDコードを実装することを想定して、ロードおよび保存用のAPIを提供します。 [〜#〜] orm [〜#〜] フレームワークを使用することを検討することもできます。これにより、ハードワークを実行できます(フレームワークは、通常、ロードと保存の操作を決定します。配置する必要があります)。
データの永続化に使用できるパターンはかなりあります。 作業単位 パターン、 リポジトリ パターン、リモートファサードのように使用できる追加のパターンなどがあります。
それらのほとんどは彼らのファンと彼らの批評家を持っています。多くの場合、それはアプリケーションに最適と思われるものを選び、それを使い続けることになります(すべての利点と欠点を持ちます。つまり、両方のパターンを同時に使用しないでください...本当に確信がない限り)。
補足として、例ではRetrieveStudent、AddStudentは静的メソッドである必要があります(インスタンスに依存しないため)。
クラス内のsave/loadメソッドを使用する別の方法は次のとおりです。
class Animal{
public string Name {get; set;}
public void Save(){...}
public static Animal Load(string name){....}
public static string[] GetAllNames(){...} // if you just want to list them
public static Animal[] GetAllAnimals(){...} // if you actually need to retrieve them all
}
個人的には、このようなアプローチをかなり小さなアプリケーション、おそらく個人用のツール、またはオブジェクトの保存またはロードよりも複雑な使用例がないことを確実に予測できる場合にのみ使用します。
また、個人的には、作業単位パターンを参照してください。あなたがそれを知るようになれば、それは小さなケースと大きなケースの両方で本当に良いです。また、EntityFrameworkやRavenDBなどの多くのフレームワーク/ APIでサポートされています。
オブジェクトがデータストアに多かれ少なかれ関連付けられている非常にシンプルなアプリの場合(つまり、データストアのプロパティと見なすことができます)、そのクラスの.save()メソッドを使用することは意味があります。
しかし、それはかなり例外的だと思います。
むしろ、通常はクラスにそのデータとその機能(優れたOO citizen)など)を管理させ、永続化メカニズムを別のクラスまたはクラスのセットに外部化する方がよいでしょう。
もう1つのオプションは、永続化を宣言的に定義する永続化フレームワークを使用することです(注釈を使用するなど)が、それでも永続化を外部化しています。