Как построить два объекта друг с другом в качестве параметра / члена - PullRequest
1 голос
/ 16 декабря 2008

У меня есть два класса, каждый из которых нуждается в экземпляре друг друга, чтобы функционировать. Обычно, если объекту нужен другой объект для запуска, мне нравится передавать его в конструкторе. Но я не могу сделать это в этом случае, потому что один объект должен быть создан перед другим, и поэтому второй объект не существует для передачи конструктору первого объекта.

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

backend = new Backend();
panel = new Panel(backend);
backend.setPanel();

Я никогда не изучал MVC; Я полагаю, что здесь я имею дело с моделью (Backend) и представлением или контроллером (Panel). Любое понимание здесь я могу получить от MVC?

Ответы [ 6 ]

8 голосов
/ 16 декабря 2008

Пора взглянуть на MVC. :-) Когда у вас есть ситуация с моделью-представлением-контроллером, все согласны с тем, что модель не должна знать о контроллере представления (MVC часто проигрывается как M-VC), но представление неизменно осознает модель .

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

2 голосов
/ 16 декабря 2008

Прежде всего, вопреки тому, что здесь сказали другие, нет никаких внутренних проблем с циклическими ссылками. Например, ожидается, что объект Order будет иметь ссылку на объект Customer лица, разместившего Заказ. Точно так же для объекта Customer было бы естественно иметь список размещенных им заказов.

В языке, основанном на ссылках (таком как Java или C #), вообще нет проблем. В языке, основанном на значениях (например, C ++), вы должны позаботиться о его разработке.

Тем не менее, вы дизайн:

backend = new Backend();
panel = new Panel(backend);
backend.setPanel(panel);

Это практически единственный способ сделать это.

2 голосов
/ 16 декабря 2008

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

Использование сеттера / конструктора действительно является частью контракта между двумя классами и фабрикой, поэтому я бы просто использовал тот, который удобнее.

Как уже указывалось, вам действительно следует попытаться найти некруглое решение.

1 голос
/ 16 декабря 2008

Лучше избегать циклических ссылок. Я лично попытался бы переосмыслить свои объекты.

0 голосов
/ 07 января 2009

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

Остальная часть этого ответа будет зависеть от языка, так как я использую Java.

Я не очень беспокоился о «JavaBeans», но я обнаружил, что следующие базовые соглашения JavaBean были очень полезны для меня в прошлом: в основном, использование стандартных методов получения и установки для свойств. Оказывается, есть соглашение JavaBean, о котором я не знал, которое действительно поможет: связанные свойства. Связанные свойства - это свойства, доступные через стандартные методы получения и установки, которые запускают PropertyChangeEvents при их изменении. [Я не знаю точно, но стандарт JavaBeans может указывать, что все свойства должны быть «связанными свойствами». На данный момент не относится ко мне. Помните также, что «стандартные» методы получения и установки могут быть очень нестандартными из-за использования классов BeanInfo для определения точного интерфейса JavaBean, но я также никогда не использую это.] (Основное другое соглашение JavaBean, которому я хочу следовать или в каждой ситуации не уместен конструктор без аргументов, я уже следую за ним в этом проекте, потому что каждый из этих объектов Backend должен быть сериализуемым.)

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

Сейчас все мои бэкэнд-объекты наследуются от общего класса Model, который обеспечивает несколько вещей, которые нужны каждому бэкенду в этой системе, включая поддержку сериализации. Я собираюсь создать дополнительный класс JavaBean как суперкласс Model, который обеспечит необходимую мне поддержку PropertyChangeEvent, унаследованную каждой Моделью. Я обновлю сеттеры в каждой модели, чтобы при вызове вызывать PropertyChangeEvent. У меня также может быть JavaBean, унаследованный парой классов, которые технически не являются моделями в том же смысле, что и эти, но которые также могут получить пользу от регистрации других классов в качестве слушателей для них. Класс JavaBean может не полностью реализовывать спецификацию JavaBean; как я уже сказал, есть несколько деталей, которые меня не волнуют. Но этого достаточно для этого проекта. Похоже, я мог бы получить все это, унаследовав от java.awt.Component, но это не компоненты в каком-то смысле, который я могу оправдать, поэтому я не хочу этого делать. (Я также не знаю, что это может повлечь за собой.)

После того, как каждая модель станет JavaBean, в комплекте с поддержкой PropertyChangeEvent, я сделаю много очистки кода: модели, которые в настоящее время хранят ссылки на панели, будут обновлены, и панели будут регистрироваться как слушатели. Так много чище! Модель не должна знать (и не должна знать в первую очередь), какие методы должна вызывать Panel при обновлении свойства.

0 голосов
/ 16 декабря 2008
panel = new Panel(backend);

Вы делаете это в этой процедуре что-то вроде

  Public Sub Panel(ByVal BackEnd as BackEnd)
        Me.MyBackEnd = BackEnd
        BackEnd.MyPanel = Me
  End Sub

Вам не нужен BackEnd.SetPanel

Лучше использовать прокси. Прокси связывает один объект с другим посредством создания события. Родитель вручает ребенку доверенное лицо. Когда ребенку нужен родитель, он вызывает метод GetRef на прокси. Затем прокси вызывает событие, которое родитель использует, чтобы вернуть себя прокси, который затем передает его дочернему элементу.

Использование механизма Event / Delegate позволяет избежать проблем с циклической ссылкой.

Итак, у вас есть (при условии, что бэкэнд здесь является «родителем»)

  Public Sub Panel(ByVal BackEnd as BackEnd)
        Me.MyBackEnd = BackEnd.Proxy
        BackEnd.MyPanel = Me
  End Sub

  Public Property MyBackEnd() as BackEnd
     Set (ByVal Value as BackEnd)
        priBackEndProxy = BackEnd.Proxy
     End Set
     Get
        Return priBackEndProxy.GetRef
     End Get
  End Property

Вот более полное обсуждение проблемы круговых ссылок. Хотя он сфокусирован на исправлении в Visual Basic 6.0.

Динамическое выделение памяти

Также еще одним решением является объединение Panel и BackEnd в другой объект. Это обычное явление, если оба элемента являются элементами управления пользовательского интерфейса и должны вести себя согласованно.

Наконец, что касается MVC, я рекомендую вместо этого использовать подход Model View Presenter.

По сути, ваша форма реализует интерфейс IPanelForm. Он регистрируется с помощью класса Panel, который выполняет всю логику пользовательского интерфейса. BackEnd должен иметь события, которые Panel может подключить при изменении модели. Panel обрабатывает событие и обновляет форму через интерфейс IPanelForm.

  1. Пользователь нажимает кнопку

  2. Форма переходит на Panel, которую пользователь нажал на кнопку

  3. Панель обрабатывает кнопку и извлекает данные из бэкэнда

  4. Панель форматирования данных.

  5. Panel использует интерфейс IPanelForm для отображения данных в форме.

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