0

アプリケーション レベルで多対多の関係でいくつかの単純なリストを管理しようとしています。オブジェクト間で接続が確立されるたびに、両方のオブジェクトの関係を更新しようとしています (例: teacher.AddStudent()) student.RemoveTeacher()

SQL を使用していた場合StudentTeacher、関係を管理するためにテーブルのようなものを使用することになることは承知していますが、アプリケーション内でこれを行う方法を理解しようとしています。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace StudentTeacher
{
    // create simple interface
    public interface IPerson {}

    // abstract class
    public abstract class Person : IPerson 
    {
        public int Id { get; set; }
    }

    // concrete "Teacher"
    public class Teacher : Person
    {
        // private list of student ids
        private List<int> _studentIds { get; set; }

        // readonly accessor
        public ReadOnlyCollection<int> StudentIds {
            get { return _studentIds.AsReadOnly(); }
        }

        // constructor
        public Teacher(int id) {
            Id = id;
            _studentIds = new List<int>();    
        }

        // add a student
        public void AddStudent(Student student)
        {
            if (!_studentIds.Contains(student.Id)) {
                _studentIds.Add(student.Id);
                student.AddTeacher(this);
            }
        }

        // remove a student
        public void RemoveStudent(Student student)
        {
            if (_studentIds.Contains(student.Id)) {
                _studentIds.Remove(student.Id);
                student.RemoveTeacher(this);
            }
        }

        public string ToString() {
            return String.Format("Id:{0} | StudentIds:{1}", Id, String.Join(",", StudentIds));
        }
    }

    /// concrete "Student"
    public class Student : Person
    {

        // private list of teacher ids
        private List<int> _teacherIds { get; set; }

        // readonly accessor
        public ReadOnlyCollection<int> TeacherIds {
            get { return _teacherIds.AsReadOnly(); }
        }

        // constructor
        public Student(int id) {
            Id = id;
            _teacherIds = new List<int>();    
        }

        // add a teacher
        public void AddTeacher(Teacher teacher)
        {
            if (!_teacherIds.Contains(teacher.Id)) {
                _teacherIds.Add(teacher.Id); 
                teacher.AddStudent(this);
            }
        }

        // remove teacher
        public void RemoveTeacher(Teacher teacher)
        {
            if (_teacherIds.Contains(teacher.Id)) {
                _teacherIds.Remove(teacher.Id);
                teacher.RemoveStudent(this);
            }
        }

        // simple override
        public string ToString() {
            return String.Format("Id:{0} | TeacherIds:{1}", Id, String.Join(",", TeacherIds));
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // create teachers
            var teacher1 = new Teacher(101);
            var teacher2 = new Teacher(102);

            // create students
            var student1 = new Student(501);
            var student2 = new Student(502);
            var student3 = new Student(503);

            // create some relationships
            teacher1.AddStudent(student1);
            teacher1.AddStudent(student2);
            teacher1.AddStudent(student3);

            // see what's happening
            Console.WriteLine(teacher1.ToString()); // "Id:101 | StudentIds:501,502,503"
            Console.WriteLine(student1.ToString()); // "Id:501 | TeacherIds:101"
            Console.WriteLine(student2.ToString()); // "Id:502 | TeacherIds:101"
            Console.WriteLine(student3.ToString()); // "Id:503 | TeacherIds:101"

            // update student1, student2
            student1.AddTeacher(teacher2);
            student2.AddTeacher(teacher2);

            // -- Outputs 
            Console.WriteLine(student1.ToString()); // "Id:501 | TeacherIds:101,102"
            Console.WriteLine(student2.ToString()); // "Id:502 | TeacherIds:101,102"

            Console.ReadLine();
        }
    }
}

私には、反対方向の add/remove メソッドを直接呼び出すのは適切ではありません。

if (!_studentIds.Contains(student.Id)) {
    _studentIds.Add(student.Id);
    student.AddTeacher(this); // calling this directly feels bad
}

これはすぐにめちゃくちゃになってしまう気がします。私の質問は次のとおりです。

  • などの静的オブザーバーのようなものを追加 して、StudentTeacherObserver代わりにイベントを発生させる必要が ありますか?OnTeacherAddedOnStudentAdded
  • そのような関係を管理するための特定のパターンはありますか?
  • この目的に適したより良いデータ構造はありますか?
4

2 に答える 2

0

ネストされたオブジェクト グラフ ( Teacher.Students[0].Teacher...) は、特にリレーションを遅延読み込みするか (おしゃべり)、すべてのオブジェクトを前もって読み込んでから参照をマッピングするか (脂肪) を決定するときに、「ごちゃごちゃ」に感じることがよくあります。

とはいえ、あなたが考えすぎているとは思いません。多対多を適切に表現するには、データ アクセスに関して何らかの譲歩が必要です。何かを変更する必要があるかどうかを判断するには、ビジネスの問題をもう少し深く評価して、より具体的なシナリオを見つける必要があると思います。

于 2012-11-20T03:10:39.267 に答える
0

最も簡単なことは、勝者を選ぶことです。したがって、両方のシナリオを実装しないでください。背景を少し説明したリンクを次に示します。

http://www.ebenroux.co.za/post/2009/09/09/Many-to-Many-Aggregate-Roots.aspx

あなたはあなたのために働くものを考え出す必要があります. 実際のオブジェクトを表現することも、必ずしも良い考えではありません。関連する ID を保存するだけで十分な場合があります。Order/ OrderItemtoのProduct例では。集約の一部ではないため、実際のProductオブジェクトを に含める理由はおそらくありません。からのデータのみを使用してから、で非正規化状態になります。が実行される (誰かが実際に を埋め始める)までは、変更することができます。もちろん、プロセスの停止 (注文のキャンセル) や製品の返品などに関するビジネス ルールは複雑になる可能性がありますが、それは状況やビジネスごとに異なります。OrderItemOrderProductOrderItemOrderOrder

ポイントは、オブジェクト グラフを「カット」して、集計を明確に定義できるようにすることです。

これは、遅延読み込みビットに役立ちます。私は遅延読み込みを決して使用せず、私が会う人には、それを避けるために最善を尽くすように促します. アイデアは実際にはオブジェクト/ドメイン モデルを照会することではありませんが、それはまったく別の議論です。

これが理にかなっていることを願っています:)

于 2012-11-20T05:20:08.793 に答える