6 ms·
One of the many reasons to prefer static factories to constructors.
by oweiler 4y ago
One of the many reasons to prefer static factories to constructors.
- mcculley 4y agoHow does that help the implementor of a subclass?
- cratermoon 4y agoOne of the many reasons to prefer composition over inheritance.
- layer8 4y agoAlternatively (in particular for subclassing), it can always be worked around with an intermediate object. For example: Constructor(A a, B b) { … } Constructor(C c, D d) { // A and B can be derived from C and D, and // we want to forward to the (A, B) constructor, // so we use an intermediate object to do the // conversion and provide A and B: this(new Intermediate(c, d)); } private Constructor(Intermediate x) { this(x.a, x.b); } Of course, with the JEP this will become much simpler.
- vips7L 4y agoI personally don't like static factories that much. They have their place, but half the JDK is now `.of` or `.from` or `.newInstance` or `.get`. I'd really like to be able to define a constructor on sealed interfaces: sealed interface Path permits WindowsPath, UnixPath { public Path(String p) { if (isWindows()) { return new WindowsPath(p); } return new UnixPath(p); } } That way user code to construct an object is always the same: var p = new Path(s); Instead of: var p = Paths.get(s);
- layer8 4y agoAn important benefit of static constructor methods is that they allow you to switch class implementations between a final class and an abstract class with private subclasses, without breaking compatibility. With regular constructors, you are prevented from switching to subclass implementations later. Static constructor methods thus decouple client code more from implementation decisions. Another benefit is being able to name different constructors differently. If I were to design a new OO language, I would make it so that constructors always work like that in the first place, from the caller perspective.
- vips7L 4y ago> An important benefit of static constructor methods is that they allow you to switch class implementations between a final class and an abstract class with private subclasses, without breaking compatibility. Is this not what I'm describing? Using one way to construct objects would allow you to change from a concrete class to an interface without breaking client code. I just want the syntax to be the same across interface/abstract/concrete so class construction is uniform and discoverable.
- layer8 4y agoI guess yes, although it now means you always need to start with interface plus class to later have the flexibility, even if you’ll never need it. It is kind of even more verbose. In current Java, you can start with a final class with static constructor methods (which is only slightly verbose), and later switch to having subclasses. It also doesn’t provide the flexibility in naming.
- vips7L 4y agoI don't see how it would be more verbose. // you can start with a final concrete class final class A {} var a = new A(); // and later move to interfaces or abstract types sealed interface A permits B, C { public A() { ... } } var a = new A(); Client code would remain the same with the flexibility to refactor to whatever you want later.
- layer8 4y agoI see, yes, if it is possible to make this binary-compatible in the JVM, that would work. One implication is that `new A()` wouldn't be guaranteed to return a new instance anymore (just as for a static method). Personally I still prefer having the ability to name the constructors. I'd rather have "new" be the default constructor name, with the option of having it be something else (i.e. Path.new(...) vs. Path.somethingElse(...)).
- vbezhenar 4y agoI have two issues with constructors. 1. They don't have a name. That's bad because names are important. It's OK if constructor is primitive and does not do anything but field assignment. But often constructors do something and naming might help. 2. There can't be two constructors with identical types (as a consequence that constructors don't have a name). This might be restricting in some cases. Factory methods solve both issues. If anything, I'd prefer to remove constructors completely and use factory methods for anything. Not realistic with Java, of course. But in my code I'm trying to follow this pattern. Constructors are absolutely minimal (usually just initializers for final fields) and any non-trivial code goes into factory method. Of course it causes issues with subclassing, so it's not a silver bullet.
- vips7L 4y agoIn practice the names are just not descriptive (of, from, newInstance, get) and I suspect this is because they're typically used to dynamically return implementations at runtime so you don't know the name of thing you want. If you know the name of the thing you want ahead of time, isn't the constructor of the named type good enough?
- _benedict 4y agoA good example might be static constructors that indicate the semantics of the parameters, such as of(array), copyOf(array), ofSorted(array) Where perhaps the first wraps, the second copies and the third may wrap and avoid sorting the input. This would all of course depend on some conventions on meaning within the codebase.