Дизайн: Обратный звонок в родительский класс

13

При моделировании объекта с дочерними элементами обычно включают дочерние элементы посредством композиции в качестве члена родительского класса. Однако иногда детям нужно что-то сказать родителю, им нужно вызвать функцию родителя. Как это можно сделать с помощью C ++? Некоторые варианты:

  1. Сделайте родительский класс глобальным, поэтому дочерние объекты смогут вызывать функции-члены родительского объекта.

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

Каковы другие способы сделать это? Есть ли общий шаблон дизайна или название для такого рода вещей?

Обратите внимание, что мне интересны идеи и решения в C ++, потому что детали будут отличаться в других объектно-ориентированных языках. Например, пункт 2 выше упоминает «указатели или ссылки», и оба возможны только в C ++. C ++ имеет языковые функции, которых нет в других языках, поэтому реализации решения проблемы потенциально могут включать эти языковые функции, что отличает решение от того, что кто-то может придумать на другом языке.

sashang
источник
можешь добавить пример? Это правильный пример для вашего вопроса? У вас есть orderobject (= parent) с элементами заказа (дочерние элементы) и вы хотите обновить итоговую сумму заказа при изменении количества элемента заказа? или думаете о чем-то совершенно ином?
k3b
@ k3b Да, это верный пример. Некоторая информация о ребенке изменилась, и теперь родитель должен что-то сделать.
Сашанг
просто добавьте несколько параметров конструктора и опорных членов данных в каждый дочерний класс.
tp1
Нужно ли ребенку знать все о родителе или достаточно простого delegate?
Жюльен Герто,

Ответы:

16

Перво-наперво, это может быть запах кода. Смысл использования композиции для родителя / детей заключается в том, что родитель знает о детях, но не наоборот. Особенно, если отношение больше «содержит», чем «состоит из».

Ссылка на родителя возможна и довольно распространена в C ++. На других языках функциональный объект или событие чаще используются, чтобы позволить ребенку сообщать то, что посторонние могут захотеть узнать. Это распространенный тип публикации-подписчика. Я подозреваю, что то, что является более идиоматическим, зависит от того, какую версию C ++ вы используете и стандарты вашей кодовой базы.

Telastyn
источник
В моей ситуации это кодовый запах. Отличный ответ.
Мартин Пфеффер
3

Как уже отмечали другие, основная идея внедрения родительского объекта в качестве указателя или ссылки - это путь в принципе.

Это имеет один недостаток: вы получаете циклическую зависимость между родителем и потомком. Если вы хотите избежать этого, определите абстрактный базовый класс (интерфейс), IParentот которого наследуется ваш родительский класс . IParentдолжен содержать методы как виртуальные функции, которые ребенок хочет вызвать. Затем введите родительский элемент как ссылку на IParent. Это значительно упрощает модульное тестирование дочернего объекта, поскольку теперь вы можете легко заменить родительский объект фиктивным объектом.

Если вашему ребенку нужно вызвать только одну функцию вашего родительского объекта, полный IParentкласс может быть больше. В этом случае будет достаточно ввести указатель на функцию-член в дочерний элемент или объект-функтор, инкапсулирующий эту функцию-член.

Док Браун
источник
2

Что вы можете сделать, так это сохранить ссылку на родителя в дочернем классе по составу. Таким образом, ребенок знает своего родителя и может вызывать публичные методы для него.

Поэтому я бы выбрал вариант 2. Вы должны быть осторожны, хотя, когда дочерний элемент удаляется из родительского элемента, вам необходимо удалить ссылку на родительский элемент в дочернем элементе и указать на ноль (или новый родительский элемент, если он имеет один). Или вы можете просто удалить дочерний объект, в зависимости от контекста.

марко-fiset
источник
1

Передайте им ссылку или указатель на родителя. Вы можете сделать их друзьями родителя или сделать вызываемый метод общедоступным. Если вы не хотите делать ничего из вышеперечисленного, вы можете передать им объект «bridge», который предоставляет один из методов родителя как public и сам является вложенным классом родителя (поэтому он имеет доступ ко всем родительским методам). ). Однако во многих ситуациях это может быть слишком сложно.

quant_dev
источник
1

2) Вставить родительский объект как указатель или ссылку в каждый дочерний объект. Затем, когда ребенку нужно что-то сказать родительскому объекту, он всегда может сделать это, потому что у него есть переменная-член, которую он может использовать.

Это вполне жизнеспособный вариант. Все современные языки имеют функцию, которая может быть использована для ссылки на другой язык.

DeadMG
источник
1

Существует аналогичный подход с небольшим разбросом, но он дает преимущества:

Скажем, родитель A содержит компонент C.

В Компоненте C объявите InterfaceC и сохраните ссылку на него. Это интерфейс компонента с внешним миром.

Родитель A реализует InterfaceC и устанавливает свою ссылку в Компоненте C. Компонент C видит Родителя A как InterfaceC.

Идея такова: компонент общается с внешним миром, используя свой интерфейс.

Преимущества использования этого по сравнению с установкой родителя напрямую:

Скажем, компонент что-то делает, и он должен уведомить родителя. Это вызывает интерфейс. Позже вы решаете, что хотите сменить родителя. Компоненту все равно, и вы не будете вносить в него никаких изменений.

Скажем позже, вы хотите уведомить многие объекты о событии. Вы просто создаете список InterfaceC и добавляете ссылки на него.

Недостатки: родительский класс в конечном итоге реализует много интерфейсов (хотя я считаю это преимуществом, поскольку, посмотрев объявление класса, я сразу узнаю, кто с ним разговаривает)

Makketronix
источник
0

Обратите внимание, что это зависит от C #. Я не знаю, есть ли в C ++ нечто подобное.

Если у вас есть графический интерфейс с кнопками, у вас обычно есть другой подход, использующий Event-Subscribetion, также известный как Observer_pattern или шаблон Publish – subscribe .

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

Помимо gui-s этот механизм может использоваться в любых отношениях между парнем и ребенком.

k3b
источник