Полтергейст (программирование).
Неуместный короткоживущий объект
В программировании полтергейст (или цыганская кибитка) — это короткоживущий, обычно не имеющий состояния объект, используемый для выполнения инициализации или для вызова методов в другом, более постоянном классе. Он считается антипаттерном. Исходное определение принадлежит Майклу Акройду, данное на конференции Object World West в 1996 году:
Подобно тому как цыганская кибитка или полтергейст загадочно появляются и исчезают, так же ведёт себя и этот короткоживущий объект. Как следствие, код становится труднее сопровождать, и возникает излишний расход ресурсов. Типичная причина этого антипаттерна — плохой объектный дизайн.
Полтергейст часто можно опознать по его имени; в них нередко встречаются такие слова, как «Manager», «Controller», «Supervisor», «StartProcess» и т. п.
Иногда классы-полтергейсты создаются потому, что программист предвосхищал потребность в более сложной архитектуре. Например, полтергейст возникает, если один и тот же метод выступает одновременно и клиентом, и инициатором в паттерне «Команда», и программист предполагает разделить эти две фазы. Однако такая более сложная архитектура в действительности может так и не появиться.
Полтергейсты не следует путать с долгоживущими, несущими состояние объектами паттернов, таких как модель—представление—контроллер, или паттернов, разделяющих уровни, таких как паттерн «Бизнес-делегат».
Чтобы устранить полтергейст, удалите класс и перенесите его функциональность в вызываемый класс, возможно, посредством наследования или в виде примеси (mixin).
Были предложены методы обнаружения полтергейстов в коде для целей рефакторинга.
Пример
Класс Poltergeist в этом примере на C++ можно рассматривать как «объект-полтергейст», поскольку он не добавляет дополнительной функциональности или инкапсуляции, а лишь увеличивает сложность за счёт ненужной абстракции.
import std;
using String = std::string;
// Класс-полтергейст, который просто хранит указатель, но не добавляет осмысленного поведения
class Poltergeist {
private:
String* s; // указатель на строку, но сам класс не делает ничего полезного
public:
explicit Poltergeist(String* s):
s{s} {}
~Poltergeist() {
delete s;
}
[[nodiscard]]
String get() const noexcept {
return s;
}
// Никакого дополнительного поведения или осмысленной функциональности
};
int main() {
// Создаём объект Poltergeist, который просто хранит указатель на строку
Poltergeist p(new String("Hello, world!"));
// Просто передаёт данные дальше, не добавляя ценности
std::println(*p.get());
return 0;
}
Вместо этого правильнее было бы воспользоваться умным указателем.
import std;
using String = std::string;
template <typename T>
using UniquePtr = std::unique_ptr<T>;
// Используем умные указатели напрямую для управления памятью
UniquePtr<String> s = std::make_unique<String>("Hello, World!");
std::println(*s);
Ещё один пример объекта-полтергейста / цыганской кибитки — следующий, где UserCreator создаётся лишь для выполнения нескольких простых действий.
import std;
using String = std::string;
class UserManager {
public:
void createUser(const String& name) {
std::println("User created: {}", name);
}
};
// Класс-полтергейст
class UserCreator {
public:
explicit UserCreator(const String& name) {
UserManager manager;
manager.createUser(name);
}
};
int main() {
// Создаём полтергейст лишь для того, чтобы вызвать createUser()
UserCreator("Alice");
UserCreator("Bob");
}
Это можно было бы более уместно сделать так, полностью избежав класса-полтергейста:
// Полностью избегаем полтергейста UserCreator
int main() {
UserManager manager;
manager.createUser("Alice");
manager.createUser("Bob");
}