Как Вы повреждаете круговые ассоциации между объектами?

Если вы используете Spring Framework:

public static void main(String[] args) {
    String uri = "http://youhost.com/test?param1=abc&param2=def&param2=ghi";
    MultiValueMap<String, String> parameters =
            UriComponentsBuilder.fromUriString(uri).build().getQueryParams();
    List<String> param1 = parameters.get("param1");
    List<String> param2 = parameters.get("param2");
    System.out.println("param1: " + param1.get(0));
    System.out.println("param2: " + param2.get(0) + "," + param2.get(1));
}

Вы получите:

param1: abc
param2: def,ghi
13
задан Erik Funkenbusch 8 March 2009 в 01:12
поделиться

8 ответов

Благодаря всем, кто ответил, некоторые действительно интересные подходы! В конце я должен был сделать что-то в большой спешке, таким образом, это - то, что я придумал:

Представленный третий объект, названный WellKnownContact и соответствующий WellKnownContactType перечисление:

public class Company
{
    public string Name { get; set; }
    public IList<Employee> Employees { get; private set; }
    private IList<WellKnownEmployee> WellKnownEmployees { get; private set; }
    public Employee ContactPerson
    {
        get
        {
            return WellKnownEmployees.SingleOrDefault(x => x.Type == WellKnownEmployeeType.ContactPerson);
        }
        set
        {                
            if (ContactPerson != null) 
            {
                // Remove existing WellKnownContact of type ContactPerson
            }

            // Add new WellKnownContact of type ContactPerson
        }
    }
}

public class Employee
{
    public Company EmployedBy { get; set; }
    public string FullName { get; set; }
}

public class WellKnownEmployee
{
    public Company Company { get; set; }
    public Employee Employee { get; set; }
    public WellKnownEmployeeType Type { get; set; }
}

public enum WellKnownEmployeeType
{
    Uninitialised,
    ContactPerson
}

Это чувствует себя немного громоздким, но обходит проблему циклической ссылки и отображается чисто на DB, который сохраняет попытку заставить LINQ к SQL делать что-либо слишком умное! Также допускает несколько типов 'известных контактов', который определенно прибывает в следующий спринт (так не действительно YAGNI!).

Интересно, как только я придумал изобретенный пример Компании/Сотрудника, он сделал НАМНОГО легче думать о, в отличие от довольно абстрактных объектов, с которыми мы действительно имеем дело.

0
ответ дан 1 December 2019 в 19:15
поделиться

Ваше решение кажется довольно разумным.

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

5
ответ дан 1 December 2019 в 19:15
поделиться

Прием к решению этого должен понять, что родитель не должен знать обо всех методах ребенка, и что ребенок не должен знать все методы родителя. Поэтому можно использовать Интерфейсный Принцип Сегрегации для разъединения их.

Короче говоря, Вы создаете интерфейс для родителя, который имеет только те методы, в которых нуждается ребенок. Вы также создаете интерфейс для ребенка, который имеет только те методы, в которых нуждается родитель. Тогда у Вас есть родитель, содержат список дочерних интерфейсов, и у Вас есть дочерняя точка назад к родительскому интерфейсу. Я называю это Зеркально отраженный Шаблон Flob , потому что диаграмма UML имеет геометрию Eckles-иорданского триггера (Предъявите иск мне, я - старый инженер аппаратного обеспечения!)

  |ISystemMenu|<-+    +->|IMenuItem|
          A    1  \  / *     A
          |        \/        |
          |        /\        |
          |       /  \       |
          |      /    \      |
          |     /      \     |
    |SystemMenu|        |MenuItem|

Уведомление, что нет цикла в этой схеме. Вы не можете запустить в одном классе и следовать за стрелками назад к Вашей начальной точке.

Иногда, для получения разделения просто право, необходимо переместить некоторые методы. Мог бы быть код, что Вы думали, должен был быть в SystemMenu, который Вы перемещаете в MenuItem, и т.д. Но в целом техника работает хорошо.

25
ответ дан 1 December 2019 в 19:15
поделиться

Возможно, самосправочный шаблон Составного объекта GoF является порядком здесь. Меню имеет набор листа MenuItems, и у обоих есть единый интерфейс. Тем путем можно составить Меню из Меню и/или MenuItems. Схема имеет таблицу с внешним ключом, который указывает назад на его собственный первичный ключ. Работы с обходом меню тот путь, также.

2
ответ дан 1 December 2019 в 19:15
поделиться

Я действительно не вижу Вашей проблемы. Очевидно Вы используете C#, который содержит объекты как ссылки не экземпляры. Это означает, что прекрасно подходит, чтобы иметь перекрестные ссылки или даже самоссылку.

в C++ и других языках, где объекты являются большим количеством compositied тогда, у Вас могут быть проблемы, которые обычно решаются с помощью ссылок или указателей, но C# должен быть прекрасным.

[еще 114], чем, вероятно, Ваша проблема то, что Вы пытаетесь следовать за всеми ссылками так или иначе, ведя к циклической ссылке. LINQ использует ленивую загрузку для решения этой проблемы. Например, LINQ не загрузит Компанию или Сотрудника, пока Вы не сошлетесь на него. Просто необходимо избежать после таких ссылок далее, чем один уровень.

Однако Вы не можете действительно добавить две таблицы как каждый внешний ключ других, иначе Вы никогда не были бы в состоянии удалить любую запись, начиная с удаления сотрудника потребует удаления компании сначала, но Вы не можете удалить компанию, не удаляя сотрудника. Как правило, в этом случае Вы только использовали бы тот в качестве реального внешнего ключа, другой просто будет psuedo-FK (то есть, тот, который используется в качестве FK, но не включил ограничения). Необходимо решить, который является более важными отношениями.

В примере компании, Вы, вероятно, хотели бы удалить сотрудника, но не компанию, поэтому сделайте компанию-> сотрудник FK ограничительные отношения. Это препятствует тому, чтобы Вы удалили компанию, если существуют сотрудники, но можно удалить сотрудников, не удаляя компанию.

кроме того, постарайтесь не создавать новые объекты в конструкторе в этих ситуациях. Например, если Ваш объект Сотрудника создаст новый объект Компании, который включает нового сотрудника ojbect созданный для сотрудника, то он в конечном счете исчерпает память. Вместо этого передайте объекты, уже созданные конструктору, или установите их после конструкции, возможно при помощи initalization метода.

, Например:

Company c = GetCompany("ACME Widgets");
c.AddEmployee(new Employee("Bill"));

тогда, в AddEmployee, Вы устанавливаете компанию

public void AddEmployee(Employee e)
{
    Employees.Add(e);
    e.Company = this;
}
3
ответ дан 1 December 2019 в 19:15
поделиться

В коде у Вас должны быть ссылки оба способа сослаться на вещи оба пути. Но в базе данных, Вам только нужна ссылка один способ заставить вещи работать. Из-за дорожной работы соединений у Вас только должен быть внешний ключ в одной из Ваших таблиц. Когда Вы думаете об этом, каждый внешний ключ в Вашей базе данных мог быть зеркально отражен вокруг и создать и создавать циклическую ссылку. Лучше всего просто выбрать одну запись, в этом случае вероятно, ребенок с внешним ключом к родителю, и просто быть сделанными.

1
ответ дан 1 December 2019 в 19:15
поделиться

В домене управляемый смысл дизайна путь можно принять решение избежать двунаправленных отношений между объектами, где это возможно. Выберите один "совокупный корень", чтобы содержать отношения и использовать другой объект только когда навигация от совокупного корня. Я стараюсь избегать двунаправленных отношений, где это возможно. Из-за YAGNI, и это заставит Вас задать вопрос, "что было первым, курица или яйцо?" Иногда Вы будете все еще нуждаться в двунаправленных ассоциациях, затем выбирать одно из решений, упомянутых ранее.

/// This is the aggregate root
public class Company
{
    public string Name { get; set; }
    public IList<Employee> Employees { get; private set; }
    public Employee ContactPerson { get; set; }
}

/// This isn't    
public class Employee
{
    public string FullName { get; set; }
}
1
ответ дан 1 December 2019 в 19:15
поделиться

Можно осуществить внешние ключи в базе данных, где две таблицы относятся друг к другу. Два пути приходят на ум:

  1. нижестоящий столбец по умолчанию в родителе является первоначально пустым и только обновляется, как только все дочерние строки были вставлены.
  2. Вы задерживаете ограничение, проверяющее до времени фиксации. Это означает, что можно вставить сначала родителя с первоначально поврежденной ссылкой на ребенка, затем ввести ребенка. Одна проблема с задержанной ограничительной проверкой состоит в том, что можно закончить за исключениями базы данных, бросаемыми во время фиксации, которое часто неудобно во многих платформах дб. Кроме того, это означает, что необходимо знать первичный ключ ребенка перед вставкой его, который может быть неловким в установке.

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

поддержка Многого DBMS задержала ограничительную проверку. Возможно Ваш делает также, хотя Вы не упоминаете, какой DBMS Вы используете

0
ответ дан 1 December 2019 в 19:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: