独自のメソッドまたは別のクラスを介してオブジェクトを保存しますか?


38

オブジェクトを保存および取得したい場合、別のクラスを作成してそれを処理する必要がありますか、それともクラス自体で行う方が良いでしょうか?それとも両方を混ぜるか?

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
        }
    }

3
あなたのような、Active Recordのパターンにいくつかの研究を行う必要があり、この質問、この1
エリック・キング

@gnat、私は優れている尋ねることは意見「の長所と短所」を追加し、それを削除しても問題がないが、実際には、私はお勧めしますOODパラダイムに関する意味、その後基づいて考えた
アフマド

3
正解はありません。それはあなたのニーズ、好み、あなたのクラスがどのように使われるべきか、そしてあなたのプロジェクトがどこに行くのを見るかによります。正しい OO の唯一のことは、オブジェクトとして保存および取得できることです。
ダンク14

回答:


34

単一の責任原則懸念の分離および機能的な結合。これらの概念を読んだ場合、得られる答えは「それらを分離する」です。

Student「DB」クラスから分離する(またはStudentRepository、より一般的な規則に従う)単純な理由Studentは、永続性を担当するコードに影響を与えることなく、クラスに存在する「ビジネスルール」を変更できるようにすることです。その逆。

この種の分離は、ビジネスルールと永続性の間だけでなく、システムの多くの懸念事項の間でも非常に重要です。関係のないモジュールに最小限の影響で変更を加えることができます(場合によっては避けられないため)。それは、より堅牢なシステムを構築するのに役立ちます。これは、保守が容易であり、絶え間ない変更がある場合により信頼性が高くなります。

最初の例のように単一のクラス、またはDBの依存関係として、ビジネスルールと永続性を混在させることによりStudent、2つの非常に異なる懸念を結び付けます。それらは一緒に属しているように見えるかもしれません。同じデータを使用しているため、凝集性があるようです。ただし、ここで重要なのは、プロシージャ間で共有されるデータだけで凝集度を測定することはできないことです。また、プロシージャが存在する抽象化レベルも考慮する必要があります。実際、理想的な凝集のタイプは次のように説明されています。

機能的結束とは、モジュールの一部がグループ化されていることです。それらはすべて、モジュールの明確に定義された単一のタスクに寄与するためです。

そして明らかに、Studentしばらくの間検証を実行しても、それが持続することは「単一の明確に定義されたタスク」を形成しません。繰り返しになりますが、ビジネスルールと永続化メカニズムは、システムの2つの非常に異なる側面であり、優れたオブジェクト指向設計の多くの原則により、分離しておく必要があります。

Clean Architectureを読んで、単一責任の原則に関するこの話(非常によく似た例を使用)を見て、Clean Architectureに関するこの話も見ることをお勧めします。これらの概念は、このような分離の背後にある理由を概説しています。


11
ああ、しかしStudentクラスは適切にカプセル化されるべきですよね?外部クラスはプライベート状態の永続性をどのように管理できますか?カプセル化はSoCおよびSRPに対してバランスを取る必要がありますが、トレードオフを慎重に検討せずに単純にどちらかを選択することはおそらく間違っています。この難問の可能な解決策は、使用する永続化コードにパッケージプライベートアクセサーを使用することです。
アモン14

1
@amonそれほど単純ではないことに同意しますが、カプセル化ではなく結合の問題だと思います。たとえば、Studentクラスを抽象化し、ビジネスに必要なデータの抽象メソッドを使用して、リポジトリによって実装することができます。このように、DBのデータ構造はリポジトリ実装の背後に完全に隠されているため、Studentクラスからもカプセル化されています。ただし、同時に、リポジトリはStudentクラスに深く結合されています。抽象メソッドが変更された場合、実装も変更する必要があります。トレードオフがすべてです。:)
ミシェルヘンリッヒ14

4
SRPがプログラムの保守を容易にするという主張は、よくても疑わしいものです。SRPが作成する問題は、適切な名前のクラスのコレクションを持つ代わりに、その名前がクラスができることとできないことをすべて伝える(つまり、理解しやすく、保守が容易になる)ことになるということです。人々が一意の名前を選択するためにシソーラスを使用する必要があった何千ものクラス、多くの名前は似ていますが完全ではなく、そのすべてのチャフをソートするのは面倒です。IOW、まったくメンテナンスできません、IMO。
ダンク14

3
@Dunkは、クラスが利用可能な唯一のモジュール化ユニットであるかのように話します。私はSRPに続く多くのプロジェクトを見てきましたが、パッケージとライブラリを正しく使用しない場合にのみ、あなたの例が起こることに同意します。責任をパッケージに分離すると、コンテキストが暗黙的に含まれるため、クラスに自由に名前を付けることができます。次に、このようなシステムを維持するために必要なのは、変更する必要のあるクラスを検索する前に適切なパッケージにドリルダウンすることだけです。:)
ミシェルヘンリッヒ14

5
@Dunk OODの原則は、「原則として、X、Y、Zのおかげでこれを行う方が良い」と言うことができます。常に適用する必要があるという意味ではありません。人々は実用的であり、トレードオフを重視する必要があります。大規模なプロジェクトでは、利点は通常、あなたが話す学習曲線を上回るようなものですが、もちろん、それはほとんどの人にとって現実ではないので、それほど重く適用すべきではありません。ただし、SRPの軽いアプリケーションであっても、永続性をビジネスルールから分離することで、コストを超えるメリットが常に得られることに同意できると思います(スローコードを除く)。
ミシェルヘンリッヒ14

10

どちらのアプローチも単一責任原則に違反しています。最初のバージョンでは、Studentクラスに多くの責任を与え、特定のDBアクセステクノロジーに関連付けます。2番目はDB、学生だけでなく、プログラム内の他の種類のデータオブジェクトを担当する巨大なクラスにつながります。編集:3番目のアプローチは、DBクラスとクラスの間に循環依存関係を作成するため、最悪Studentです。

ですから、もしあなたがおもちゃのプログラムを書こうとしないなら、どれも使わないでください。代わりに、StudentRepository独自のCRUDコードを実装することを想定して、ロードおよび保存用のAPIを提供するためにのような別のクラスを使用します。また、ORMフレームワークを使用することを検討することもできます。ORMフレームワークは、あなたのために大変な作業を行うことができます(通常、フレームワークは、LoadおよびSave操作を配置する必要がある決定を強制します)。


ありがとう、私は私の答えを修正しました、3番目のアプローチはどうですか?とにかく私はここでポイントは明確なレイヤーを作ることだと思う
アフマド14

DBの代わりにStudentRepositoryを使用することも示唆しています(理由はわかりません!おそらくまた単一の責任です)。単なるデータアクセスレイヤーの場合、静的ユーティリティ関数の束が増え、一緒になりますか?たぶんそれはとても汚くて混雑したクラスになるでしょう(私の英語は申し訳ありません)
アフマド14

1
@Ahmad:いくつかの理由と、考慮すべき長所と短所があります。これは本全体を埋めることができます。この背後にある理由は、特にプログラムのライフサイクルが長期にわたる場合、プログラムのテスト容易性、保守容易性、および長期進化可能性に要約されます。
Doc Brown 14

DBテクノロジーに大きな変化があったプロジェクトに取り組んだ人はいますか?(大規模な書き換えなし?)私はしていません。もしそうなら、そのDBに縛られるのを避けるためにここで提案されているように、SRPに従うことは大いに役立ちましたか?
user949300 14

@ user949300:私は自分自身を明確に表現しなかったと思う。学生クラスは特定のDBテクノロジーに縛られることなく、特定のDBアクセステクノロジーに縛られるため、永続化のためにSQL DBのようなものを期待します。Studentクラスに永続化メカニズムを完全に認識させないようにすると、さまざまなコンテキストでクラスを簡単に再利用できます。たとえば、テストコンテキスト、またはオブジェクトがファイルに保存されているコンテキスト。そして、それは私が過去に非常に頻繁にやったことです。このメリットを得るために、システムのDBスタック全体を変更する必要はありません。
Doc Brown 14

3

データの永続化に使用できるパターンはかなりあります。ある作業の単位パターンがあり、リポジトリ上と上のようにリモートファサードのように使用することができ、いくつかの追加のパターンがあり、パターンが。

それらのほとんどには、ファンと批評家がいます。多くの場合、アプリケーションに最も適していると思われるものを選択し、それにこだわることになります(すべての長所と短所、つまり、両方のパターンを同時に使用しないことです...本当に確信がない限り)。

補足説明として、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でサポートされています。


2
サイドノートについて:概念的には、アプリケーションの実行中にDBは1つだけですが、RetriveStudentメソッドとAddStudentメソッドを静的にする必要があるという意味ではありません。リポジトリは通常、開発者がアプリケーションの残りの部分に影響を与えることなく実装を切り替えることができるようにするために、インターフェースで作成されます。これにより、たとえば、さまざまなデータベースをサポートしてアプリケーションを配布することもできます。もちろん、この同じロジックは、たとえばUIなどの永続性以外の多くの領域にも適用されます。
ミシェルヘンリッヒ14

あなたは間違いなく正しいです、私は元の質問に基づいて多くの仮定をしました。
ジェリーノ14

私は、リポジトリのパターンで述べたように最善のアプローチは、レイヤーを使用していると思いますが、あなたに感謝
アフマド

1

オブジェクトがデータストアとビザにほぼ結び付けられている非常にシンプルなアプリ(つまり、データストアのプロパティと考えることができる)である場合、そのクラスに.save()メソッドを使用することは理にかなっています。

しかし、それは非常に例外的だと思います。

むしろ、通常はクラスにそのデータとその機能(良いOOシチズンのような)を管理させ、永続化メカニズムを別のクラスまたはクラスのセットに外部化する方が適切です。

もう1つの選択肢は、永続性を宣言的に定義する永続性フレームワークを使用することです(アノテーションを使用する場合など)が、それでも永続性は外部化されます。


-1
  • そのクラスでオブジェクトをシリアル化するメソッドを定義し、データベースやファイルなどに入れることができる一連のバイトを返します
  • そのようなバイトシーケンスを入力として受け取るそのオブジェクトのコンストラクターがあります

RetrieveAllStudents()方法については、あなたの気持ちは正しいです。確かに、見当違いです。複数の異なる学生のリストがあるかもしれません。リストをStudentクラスの外に単純に保持しないのはなぜですか?


LaravelなどのPHPフレームワークでこのような傾向を見たことがあります。モデルはデータベースに関連付けられており、一連のオブジェクトを返すこともできます。
アフマド14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.