You are currently viewing What’s your preferred approach/style for factory functions?

What’s your preferred approach/style for factory functions?

Let’s say we have an interface or a class with a private constructor and we want to write a factory “constructor” for it. There seems to be three-ish ways to go about it (suggest more if you know any!):

Companion object function

interface Foo { companion object { fun create(): Foo = TODO() } } 

This is the most simple and straight-forward approach, IMO. No real downside except that it might seem “noisier” at the call sites than calling something that looks like a class constructor.

“Invocable” companion object

interface Foo { companion object { operator fun invoke(): Foo = TODO() } } 

Almost the same as above. The advantage to this is that the call sites can now look like they’re calling a constructor. The downside is that passing the factory function as a function reference is uglier and “leaks” the fact that we’re trying to pretend it’s a constructor (i.e., you have to pass Foo.Companion::invoke as a reference).

Same-name free function

interface Foo {} fun Foo(): Foo = TODO() 

This one has the same advantage as the invocable companion object, and fixes its disadvantage because you’re just passing a regular function reference (::Foo).

The downsides of this come when you have a class with a private constructor. When you have a class with a private constructor, you must use one of the companion object approaches, because that’s the only way to actually call the private constructor. So, you can define this kind of free function in addition to the companion object factory, but maybe it’s not worth it.

The other problem is that the factory function cannot have the same name+parameters as the real constructor (and tricks like @JvmName don’t fix it). This isn’t often a problem because “Why wouldn’t you just make the constructor public, then?”. But it can actually be annoying if you just want to do some validation on the arguments before calling the true constructor: for example, maybe you want to return a null for invalid arguments instead of throwing an exception.

So which approach do you find yourself using the most? I use the last one most often, even though that means sometimes also having a companion object factory function. But lately, I’m starting to think I might start leaning toward being less “clever” and just using a regular, old, named factory function.

submitted by /u/ragnese
[link] [comments]