Constructors Are Evil

SmellsLikeJava and C++, too.

Constructors are overloaded anonymous methods. It's impossible to neatly make two constructors that take one string and have that string mean something different to each.

I say: banish constructors from JavaLanguage and instead declare methods to be static, instance (the default), or new. They don't even need to return this - e.g. a "constructor" that makes a new instance and adds it to a pool.

-- PaulMurray

This is exactly how object instantiation is done in "real" object oriented languages, such as SmalltalkLanguage, RubyLanguage, EiffelLanguage, PerlLanguage, etc.


Suppose there where keyword/named parameters in Java, wouldn't the problem dissolve into nothing then?


AnswerMe: When would this capability be useful? An example would be nice.

"Ok, we have a ValueObject with a String foo and a String bar"

 Obj(String foo, String bar)

"Dang! It may also have a baz, which means it won't need a bar" "What?? You mean your business database is not in third normal form?" "'Fraid so!" "Heretic! Heathen! Blasphemer!"
 Obj(String foo, String baz)

"Oops! Wont work - overloaded signature!"
 Obj(String foo, String bar, String baz) // if baz is not null, then bar may be null

replace this with, (IMO)

 class Obj {
public new Obj buildUsingBar(String foo, String bar) { ... }
public new Obj buildUsingBaz(String foo, String baz) { ... }
 ...
 }  

--

Static would work just as well here instead of new

--

Like others have said, real OO languages basically work like this already. But the other guy has a point, you're not passing in bar or baz, you're passing in string and string, so of course it doesn't work. Make a bar and baz class and wrap the string with them, and this would work fine, with existing semantics, and give you exactly the behavior you want, without requiring different constructor names, because let's face it, Java's not changing, CSharp's not changing, so what's the point.

 Obj( Foo f, String foo )
 Obj( Bar b, String foo )
 Obj( Foo f, String foo, Bar b, String bar, Baz z, String baz )

where Foo, Bar, Baz are empty classes used only as tags. By golly, you are programmers, you are supposed to be capable of creative thinking! Oh, and quit using strings everywhere. If you want Perl, you know where to find it.


Make a factory if you don't like it. Constructors are very simple. Why do you want to make it more complicated? And if you want to make the constructor polymorphic then make a real class. Quit using strings everywhere.

I want to reinforce that point about the factory. Most constructors end up needing only a few options for a new() signature, in which case, the constructor is great. In cases where the constructor is not great, create several static/class factory methods that return properly constructed objects, and make new() private.

Or - if you're in Ruby land or using Java 1.5, use argument arrays, and parse out the contents (strong typing advocates, feel free to eschew this option).

--

Actually, better languages already do that, and it's not more complicated, it's simpler. Making it a real class doesn't allow constructors to be polymorphic; you need MetaClasses for that (see SmalltalkLanguage).

I believe they where talking about ParameterObjects here. --

Regrettably, out here in the RealWorld we often build things from data in databases.

--

Um:

class CantDo
{
CantDo(string a)
CantDo(string b)
}

class CanDo { CanDo(RealClassA a) CanDo(RealClassB b) }

Oops, not sure what I was smoking. I thought he meant making constructors equal to other kinds of functions, i.e., virtual, overridable, and inheritable, something that metaclasses make natural since constructors are ordinary methods on the metaclass.


Why is it beneficial to separate object allocation and object initialization?

Coming from the world of CeeLanguage where there are no constructors, I found having a method combine memory allocation for an object with initialization of the object a significant benefit. Constructors help (but they are not perfect) avoid having a window where operations on an object cannot be called. I am not certain I understand the complaint above, and constructors are not perfect, but they are definitely a step in the right direction.

It is beneficial to separate object allocation and object initialization because you don't always want to allocate objects of a particular class in the same way. In C++, for example, you nearly always have the choice of stack based or heap based allocation. If you are working with shared memory, you may wish to allocate objects at a specific address using PlacementNew?.


Constructors are totally evil (see NewConsideredHarmful). I'm an OO guru; that means I'm an expert, not that I like it. You've got to know OO pretty damn well to really know the problems and limitations. You also need to know it in many languages, so you can distinguish language issues from those inherent to the design. (Same is true for any paradigm, I suspect. I imagine that much paradigm zealotry comes from those least experienced. The rest comes from people like you, who knows the value of a paradigm and sad to see it trampled beneath the feet of the ignorant. Unfortunately, you don't know the other paradigms well enough to make a proper argument about their limits.)

Re: "constructors are totally evil" - Well, that's an interesting generalization. I can see we are getting into philosophical issues beyond just GUI's. I'll have to ponder that one. I'm suprised nobody else challenged that one much. But there is something smelly about a bunch of routines with names like "initialize_and_display_foo()" or "update_and_display_foo()". If you see variations of "initialize_and_X" over and over, it implies an abstraction is needed, and constructors seem like the appropriate abstraction. -t

There are plenty of other abstractions available. Consider load = function(X) { show X; X.init(); }. Now you can use "load(X)" and "load(foo)", and you still aren't implicitly executing side-effects as a mere consequence of display-state (minimized vs. maximized, for example).

And the problems with constructors are that: (1) they hurt the GOD RAM illusion of having all objects in memory all the time, (2) which in turn hurts OrthogonalPersistence and TransparentDistribution... and automatic redundancy, duplication for locality of stateless objects, and a ton of other features. (3) They tend to execute while the object-configuration - or even the object - is still only half-defined. This hurts composition of objects... their constructors all need to be defined just right for them to compose. (4) There are gravely serious problems with cyclic references (e.g. for mutual interdependence) at construction time that take SelfDiscipline to avoid. (5) They harm optimizations! I.e. if you were writing an object configuration language, the use of constructors would make it extremely difficult to remove dead-code based on an object-reachability-graph; you'd also need to analyze for confinement of side-effects, which is much more difficult. (6) They are not abstract. (But see AbstractConstructor.) (7) The basic idea behind a constructor is to build the objects/data inside another one, which implies 'ownership' of that data and does not readily scale or compose.

When a feature makes you fight the language to get other features, that's a LanguageSmell. (on-display events have this same smell... but even worse due to composition and sharing issues.)

But there is a "confusion spike" in that sometimes you can just open the form/window and other times you have to call a subroutine before-hand to refresh stuff. It's usually better mental abstraction (MentalIndexability) to always just open the form and let it "handle itself". Otherwise, you have to concern yourself with the details of how its opened. Further, it reduces the chance that the form will be opened without remembering to initialize. Without your approach, one has to "just remember" to always open the form via the subroutine. With your approach, the guts are on the outside of the body. Optimization is a secondary concern to helping developers think clearer and avoid forgetfulness-related mistakes. -t

If you wished to abstract the definition of X to the point that 'show X' always executes 'X.on_display()', just like it abstracts other details (like rendering), that would still not involve on-display operations. In particular, 'show X' commands are always explicitly provided in some other input handlers. You would always be able to trace the call of 'X.on_display()' back to an original source user or server. This is different from true on-display events, which trace to a third party: the browser.

And if your objection is that people are abstracting and concerning themselves with details of how things occur, then the real problem you're complaining about is the use of ProceduralProgramming. Consider a more declarative or rules-driven approach, perhaps DataflowProgramming or FunctionalReactiveProgramming. Having a display that continuously updates based on changing external data based entirely upon a declarative description of the data-source requires even less concern for 'how' a display is opened and 'how' it maintains itself.


Please note that I didn't exactly read the whole discussion, but I do agree somewhat with the sentiment at the top of the page. I think that this might be the reason the original poster thought that "new" methods would be a good idea.

You see, the problem with constructors is that they are necessary. You need to write a ctor to initialize a class.

class Obj {
   public final String foo;
   public final String bar;
   public final String baz;
   private Obj(String foo, String bar, String baz) {
  this.foo = foo; this.bar = bar; this.baz = baz;
   }
   public static Obj withFooAndBar(String foo, String bar) { // In Objective-C, this would be withFoo: andBar:.  I'm trying to emulate that.
   return new Obj(foo, bar, null);
   }
   public static Obj withFooAndBaz(String foo, String baz) {
   return new Obj(foo, null, bar);
   }
}

Whereas with the proposed syntax, one could do this, instead:
class Obj {
public final String foo;
public final String bar;
public final String baz;
public new withFooAndBar(String foo, String bar) {
   this.foo = foo; this.bar = bar; this.baz = null;
}
public new withFooAndBaz(String foo, String baz) {
   this.foo = foo; this.bar = null; this.baz = baz;
}
}
Ironically, this syntax creates more typing, which I'm sure the original poster did not intend. It does, however, make all initializer functions use the same syntax.

Some languages which do this sort of thing easily are:

    //Scala.
    class Obj private (foo : String, bar : String, baz : String) // Never mind the fact that if bar and baz were mutually exclusive, a guru Scalite would use Either[String, String]
    object Obj {
        def withFooAndBar(foo : String, bar : String) = new Obj(foo, bar, null)
        def withFooAndBaz(foo : String, baz : String) = new Obj(foo, null, baz)
    }

In ScalaLanguage, you write the constructor as arguments to the class, with a private designation if need be. The params then become member vals. Because of some special rules in Scala, all construction of objects must be done through constructors written in this way. (You can write more constructors, but the last thing all constructors must do is call the original constructor.)

     # Perl Six.
     use v6;
     class Obj {
         has $.foo, $.bar, $.baz;
         method new(*@_, *%_) { die "Cannot instantiate Obj through new." }
         method withFooAndBar($foo, $bar) {
             bless *, :$foo, :$bar; # Bless works with all classes.  :$foo is short for :foo($foo), which in this case means "initialize member variable foo to $foo.
         }
         method withFooAndBaz($foo, $baz) {
             bless *, :$foo, :$baz;
         }
      }
As you can see, neither approach actually forces you to write a constructor, although one makes you write its signature--and that one actually saves you time, as you would have written out the member variables anyway, right?


See DeterministicResourceManagement and to ResourceAcquisitionIsInitialization for related issues about constructors.


CategoryEvil

JuneZeroNine


EditText of this page (last edited February 18, 2011) or FindPage with title or text search