oop — должна ли абстрактная фабрика C ++ предоставлять метод уничтожения для созданных объектов?

Рассмотрим следующий интерфейс (используются тупые указатели, потому что мы все еще в
С ++ 98)

class WidgetMaker {
virtual Widget* makeWidget() = 0;
};

Со следующей вероятной реализацией

class SpecificWidgetMaker: public WidgetMaker {
Widget* makeWidget() {
return new SpecificWidget();
}
};

Widget — это некоторый базовый класс с виртуальным деструктором, SpecificWidget расширяет его.
Мои коллеги утверждают, что интерфейс WidgetMaker должен содержать следующий метод

virtual void freeWidget(Widget* widget);

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

Я считаю, что такой дизайн вообще плохая идея — он усложняет клиентский код, нарушает KISS и YAGNI, затрудняет переход (маловероятный в нашей организации в ближайшие 20 лет) к unique_ptr. Должен ли я доверять своим чувствам? В каких случаях бесплатный метод как часть абстрактного фабричного интерфейса оправдан?

4

Решение

Проблема с предложенным вашим другом решением (на самом деле, также с вашим оригинальным) заключается в том, что у него есть излишне нетривиальный протокол. Как только вы получите Widget с помощью makeWidgetВы должны помнить, чтобы освободить его (либо напрямую, либо вызывая какой-либо метод фабрики). Это, как известно, хрупкое — оно либо сломается быстро (вызывая Widget утечки) или действительно усложняют клиентский код.

Если вы посмотрите на интерфейс std::shared_ptr::shared_ptr(...), Вы можете видеть, что это может занять пользовательский объект удаления.

Таким образом, возможно, вы могли бы typedef (или эквивалент), что именно Widget умный указатель:

using WidgetPtr = std::shared_ptr<Widget, ...>

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

Основным преимуществом этого является то, что он устраняет необходимость помнить, чтобы освободить Widget от пользователя.

7

Другие решения

Я бы сказал, что здесь нет общих правил. Все зависит от деталей вашего Widgetс реализацией.

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

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

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

Если вы решили пойти с freeWidget(), одна вещь, которую вы могли бы рассмотреть, это сделать деструкторы всех подклассов приватными (скорее всего, с некоторыми подходящими friend декларация так что-то может на самом деле уничтожить эти вещи), чтобы обеспечить соблюдение этой политики.

Один пример, почему вы можете захотеть иметь явный freeWidget() будут исключения. Бросать исключения из деструкторов … обидно. Это разрешено, но имеет определенные … ограничения. Итак, если есть вероятность, что уничтожение вашего виджета может вызвать исключение, используя freeWidget() позволит вам лучше контролировать создание исключения, в этом случае, и корректную очистку уничтоженного виджета перед этим.

0