2

After reading What differentiates function objects from poltergeists?, according to the definition of poltergeist, I still don't understand why would "poltergeist" be a bad pattern:

A poltergeist is a short-lived object, typically stateless, which is used solely to trigger or initialize several other objects and then discarded. It is considered a consequence of poor object design

What I don't understand is, why can't solely to trigger or initialize several other objects be a good practice of encapsulation and reuse? I think I often do "solely to trigger or initialize several other objects", for example, creating a dialog and add it to main UI:

void addWelcomeDialog(View view,UserInfo userInfo){
    Labal newNameLabel=new Label("Welcome, "+userInfo.nickname);
    view.addSubview(newNameLabel);
    .
    .
    .
    Image genderImage=new Image("icon_"+userInfo.gender+".png");
    .
    .
    .
}

new DialogHelper().addWelcomeDialog(this.glview,this.userInfo);

So addWelcomeDialog doesn't have its own state, and is just used to initialize bundles of UI objects. If it belongs to "poltergeist", was it required to delete "addWelcomeDialog", and copy and paste the codes in "addWelcomeDialog" to the place it originally calls "addWelcomeDialog"? Isn't it violating DRY principle?

And other question that mentions poltergeists : Creating an abstraction just for exception handling - a pattern or anti-pattern?

Useless classes with no real responsibility of their own, often used to just invoke methods in another class or add an unneeded layer of abstraction.

Why can't "just invoke methods in another class" be a real responsibility? For example, to draw something in a OpenGL view, the function may just invoke bundles of methods in a OpenGL view, eg:

void drawStatDiagram(GLView glview,StatData statData){
    glview.setBackgroundColor(0.25,0.25,0.25);
    glview.drawText("Graph of "+stateData.name,32,1,0,0,glview.width*0.5,glview.height*0.9);
    .
    .
    .
    //call a series of glview function to display stat graph
}

new OpenGLDrawer().draw(this.glview,this.stateData);

The way and the sequence of "invoke other object" is complex and hence encapsulated into a function in a separate helper class for reuse. So whats wrong with that approach?

So I think the definition of "poltergeists" is just "object that uses once only (or "new Object() that appears once only")" or "add an unneeded layer of abstraction". Is that true? If not, what am I misunderstood about "poltergeists"?

If it is complaining about "short-lived object", isn't the definition is just "the method can be static but currently isn't"?

If it is about "Don't put the things to encapsulate in object constructor" (eg:avoid new ObjectToDoSomething(this.data)), isn't put it into a separate static method (eg:ObjectToDoSometing.doSomething(this.data)) solves the problem? (Which implies that the root cause is not "solely to trigger or initialize several other objects", but place/way to "solely to trigger or initialize several other objects")

2
  • 2
    You are asking about objects but showing examples of functions/methods. In a Java-style language, all functions must belong to a class. But it may not be appropriate to invoke them via an object instance. Patterns like new Something(arg1).method(arg2) suggest that you might want Something.method(arg1, arg2) instead. In languages with free functions, there wouldn't even have to be a class involved. Of course objects still have their uses, like encapsulating state or providing polymorphism (in particular when implementing an interface).
    – amon
    Commented Sep 19, 2023 at 4:53
  • A think to keep in mind: there are a lot of patterns (singleton comes to mind, for example) that people can't agree on if they're anti-patterns or not. Often, what makes something an anti-pattern isn't the construction or how it is used, but why it was built in the first place.
    – T. Sar
    Commented Sep 19, 2023 at 11:23

1 Answer 1

6

First, let me say the Wikipedia article about Poltergeist, as it is written today in Sept. 2023, isn't very clear. It lacks an example, suffers from some overgeneralization and oversimplification, and it misses to mention the essential condition under which such helper classes really become an anti pattern: when they are superfluous and hence useless.

Second, I think that the idea of creating helper classes being an antipattern is a vast exaggeration. Sometimes, introducing such a class, even when stateless and short-living can be a useful intermediate step for creating a better abstraction, and it is definitely better than just copy-pasting code around for which one currently does not have a better place.

Now let us look at your first example. There is nothing wrong in having a method addWelcomeDialog in your code when it can be reused in several places. The debatable part here is whether it is a good idea to make this an instance method of a "Poltergeist" class DialogHelper, or if there isn't a more readable and maintainable solution. The questions one should ask here are:

  • Isn't there a more natural place where addWelcomeDialog could "live"? You said it is for "adding a welcome dialog to your main UI", so maybe there is already a custom derivation MyView of the class View in your code - that would be a way-more obvious place for this method. Placing it there would give the method better discoverability.

  • Is addWelcomeDialog reused more then once from different callers? If not, it could stay simply part of the calling class, no helper class needed (note code for automated testing may count as a different caller - if it helps testing, a helper class may be justified).

  • In case addWelcomeDialog shall be reused by different callers, and there is no such class MyView (and you cannot introduce such a class for some reason) - is the name DialogHelper really well chosen?

  • why is addWelcomeDialog not a static member of it's class?

In a language like C#, when we cannot extend a class View of a 3rd party lib or framework directly, we typically use a class named ViewExtensions and implement addWelcomeDialog as an extension method. This is just a static method which can be syntactically called like as if it was an instance method of View. In Java, one could do almost the same, make addWelcomeDialog a static method of ViewExtensions and use the method without the extra syntactic sugar as ViewExtensions.addWelcomeDialog(...). In languages like C++, one could utilize free functions to avoid an extra helper class at all. Still one has to decide how to name the module or file where the function will to be placed, and a name like "ViewExtensions.h" is probably more meaningful than "DialogHelper.h".

Another justification for a helper class would be there if it needs some state. Lets think, for example, a class WelcomeDialogBuilder, where you want to pass some configuration parameters through the constructor and then call addWelcomeDialog multiple times with this config. That makes a WelcomeDialogBuilder a perfectly valid abstraction.

So in short, there is nothing wrong with "solely to trigger or initialize several other objects". This description is simply not a useful criterion for assessing whether some helper class is useful or not.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.