Рассмотрим следующий интерфейс (используются тупые указатели, потому что мы все еще в
С ++ 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. Должен ли я доверять своим чувствам? В каких случаях бесплатный метод как часть абстрактного фабричного интерфейса оправдан?
Проблема с предложенным вашим другом решением (на самом деле, также с вашим оригинальным) заключается в том, что у него есть излишне нетривиальный протокол. Как только вы получите Widget
с помощью makeWidget
Вы должны помнить, чтобы освободить его (либо напрямую, либо вызывая какой-либо метод фабрики). Это, как известно, хрупкое — оно либо сломается быстро (вызывая Widget
утечки) или действительно усложняют клиентский код.
Если вы посмотрите на интерфейс std::shared_ptr::shared_ptr(...)
, Вы можете видеть, что это может занять пользовательский объект удаления.
Таким образом, возможно, вы могли бы typedef
(или эквивалент), что именно Widget
умный указатель:
using WidgetPtr = std::shared_ptr<Widget, ...>
Если вы позже решите, что фабрике необходимо выполнить какое-то действие, когда Widget
освобожден, вы можете изменить typedef
к тому, который использует пользовательское средство удаления, и это пользовательское средство удаления может уведомить фабрику о том, что объект удаляется.
Основным преимуществом этого является то, что он устраняет необходимость помнить, чтобы освободить Widget
от пользователя.
Я бы сказал, что здесь нет общих правил. Все зависит от деталей вашего Widget
с реализацией.
Если все, что нужно сделать, чтобы правильно уничтожить экземпляр любого Widget
подкласс может быть обработан в своем обычном деструкторе, объявив явное freeWidget
() в вашем факторе не служит никакой полезной цели. Это не добавляет никакой ценности.
Если, с другой стороны, нужно что-то сделать, что по какой-то причине не может быть обработано в деструкторе, тогда, очевидно, вам нужны явные методы для уничтожения ваших виджетов.
Возможно, в настоящее время нет необходимости в какой-либо специальной обработке такого рода, но вы предвидите необходимость в ней в будущем. В этом случае имеет смысл объявить явное freeWidget
(), чтобы потом не переписывать кучу кода.
Если вы решили пойти с freeWidget
(), одна вещь, которую вы могли бы рассмотреть, это сделать деструкторы всех подклассов приватными (скорее всего, с некоторыми подходящими friend
декларация так что-то может на самом деле уничтожить эти вещи), чтобы обеспечить соблюдение этой политики.
Один пример, почему вы можете захотеть иметь явный freeWidget
() будут исключения. Бросать исключения из деструкторов … обидно. Это разрешено, но имеет определенные … ограничения. Итак, если есть вероятность, что уничтожение вашего виджета может вызвать исключение, используя freeWidget
() позволит вам лучше контролировать создание исключения, в этом случае, и корректную очистку уничтоженного виджета перед этим.