Какой шаблон дизайна? - PullRequest
4 голосов
/ 19 июня 2009

У меня есть класс A, ведущий список объектов класса B. Но на каждый объект класса B можно ссылаться в любом объекте класса A. Класс B также поддерживает список объектов класса A, на которые он ссылается. Программа может и будет создавать (несколько) объектов обоих классов A и B «по желанию», а также удалять их.

Если я использую C #, я могу добавлять и удалять объекты из обоих классов с помощью следующего кода

public class A
{
    private List<B>ListOfObjects_B;
    public bool Add(B Object)
    {
       bool bAdd = false;
       if ((Object != null) && (ListOfObjects_B.IndexOf(B) <0))
       {
          ListOfObjects_B.Add(Object);
          Object.Add(this);
          bAdded = true;
       }
       return bAdded;
    }

    public bool Delete(B Object)
    {
       bool bDeleted = ListOfObjects_B.Remove(Object);
       if (bDeleted == true) Object.Delete(this);
       return bDeleted;
    }
}

public class B
{
    private List<A>ListOfObjects_A;
    public bool Add(A Object)
    {
        bool bAdd = false;
        if ((Object != null) && (ListOfObjects_A.IndexOf(A) <0))
        {
            ListOfObjects_A.Add(Object);
            Object.Add(this);
            bAdded = true;
        }
        return bAdded;
   }

   public bool Delete(A Object)
   {
       bool bDeleted = ListOfObjects_A.Remove(Object);
       if (bDeleted == true) Object.Delete(this);
       return bDeleted;
   }
}

Это будет работать, поскольку из-за удаления / добавления объекта в ListOfObject ВТОРОЙ раз (по рекурсии) будет вызвана функция, которую не удастся удалить / добавить, что позволит избежать бесконечного цикла.

Но мне не нравится этот код, хотя A и B не слишком много знают о другом классе и просто вызывают функцию Delete / Add.

Я полагаю, что проблема такого рода является общей, и для ее решения существует шаблон проектирования, позволяющий избежать рекурсии, и обновление обоих списков будет «просто лучше». Какой шаблон дизайна я должен использовать? Буду признателен, если какой-то код будет добавлен.

Ответы [ 3 ]

4 голосов
/ 19 июня 2009

Вы можете упростить задачу, переместив «объектную ассоциацию» в выделенный класс. Вот что я имею в виду.

Определить класс с именем AssociationTable. Этот класс будет вести список пар, где каждая пара содержит ссылку на объект A и ссылку на объект B.

Каждый объект A (и каждый объект B) будет содержать ссылку на объект AssociationTable. A.Add (B) будет реализован как table.add (this, b); B.Add (A) будет реализован как table.add (a, this);

Удаление будет реализовано как table.delete (this, b) или table.delete (a, this)

class Pair { 
  A a; B b; 
  Pair(A a, B b) { this.a = a; this.b = b; } 
  // Also override Equals(), HashCode()
}

class AssociationTalbe {
  Set<Pair> pairs = ...;

  void add(A a, B b) { pairs.add(new Pair(a, b)); }
  void remove(A a, B b) { pairs.remove(new Pair(a, b)); }
}

class A {
  AssociationTable table;

  public A(AssociationTable t) { table = t; }

  void add(B b) { table.add(this, b); }
  void remove(B b) { table.remove(this, b); }
}

Edit: Проблема с этим дизайном - сборка мусора. таблица будет содержать ссылки на объекты, подавляя тем самым их коллекцию. В Java вы можете использовать объект WeakReference для преодоления этой проблемы. Я почти уверен, что в мире .Net что-то похожее

Кроме того, стол может быть одиночным. Я не очень люблю одиночные игры. Здесь, синглтон сделает ассоциацию A-B уникальной для всей вашей программы. Это может быть чем-то нежелательным, но это зависит от ваших конкретных потребностей.

Наконец, (просто для того, чтобы разобраться в контекстах) этот дизайн работает так же, как отношения «многие ко многим» в реляционных базах данных.

2 голосов
/ 19 июня 2009

Я недавно написал класс для решения аналогичной проблемы . На самом деле это более простой сценарий (отношения родитель / ребенок с детьми, ссылающимися на своих родителей), но вы, вероятно, могли бы адаптировать его для своих нужд. Основное отличие состоит в том, что свойство Parent в моей реализации должно быть заменено коллекцией родителей.

1 голос
/ 19 июня 2009

О единственной вещи, которую я могу придумать, это использование шаблона Mediator, чтобы A не добавлял себя в B. Вот пример:

public class Mediator {

    public void Add(A List, B Object) {
        if(list.Add(Object)) {
            object.Add(List);
        }
    }

    public void Delete(A List, B Object) {
        if(List.Delete(Object)) {
            Object.Delete(List);
        }
    }
}

После этого вы удалите строки кода, которые читают «Object.Add (this);» и "if (bDeleted == true) Object.Delete (this);" Это также дает преимущество, заключающееся в уменьшении числа вызовов каждого метода, как прежде, чем методы объекта A вызывались дважды, поскольку объект B также вызывал эти методы для объекта A.

РЕДАКТИРОВАТЬ: После дальнейшего рассмотрения я понял, что вы уже используете шаблон проектирования Observer. Объект A является наблюдателем, а объект B является наблюдаемым. Объект A ведет список объектов, за которыми он наблюдает, а объект B ведет список объектов, за которыми он наблюдает. Единственное, что я не вижу никакой дополнительной функциональности, хотя, вероятно, есть некоторые. По сути, объект B уведомит все наблюдающие его объекты A о том, что он изменился, и все эти объекты A попросят об изменении. Если это то, что вы ищете, то все, что вам нужно сделать, это удалить строки "Object.Add (this);" и "if (bDeleted == true) Object.Delete (this);" из кода B, так как он не нужен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...