DEV Community

geraldcroes
geraldcroes

Posted on • Originally published at Medium on

Kotlin Fun With The Factory Pattern

When you read “let’s have some fun with the factory pattern”, I agree that you should doubt the writer’s enthusiasm and see for yourself. But, as I was writing some code to play with this language I’ve recently fallen in love with, I asked myself, “How should I write a factory method?”

This simple question led me to some experimentations.

Ok to have some fun, but … what’s a factory?

For those in need of a reminder, the factory is a creational pattern listed in the book commonly referred to as “the GOF”. If you haven’t heard or read about it, there’s still time to catch up. If it falls in the TL;DR category, don’t hesitate to google the world of patterns; there are plenty of resources available on the internet (and Wikipedia is a satisfactory one for a quick introduction).

The factory’s goal is to deliver a ready-to-use object to its client. The factory hides the complexity of creating and selecting the right object for the job.

As we’re all fond of UML, I’ll sketch something that vaguely looks like UML (on a Saturday night) so we’re on the same page.

The Factory as it is in the GOF

Now that we all remember what a factory is (I know you knew it — but just in case), let’s write the first candid Kotlin implementation.

Say we have a product called a FileParser that we need so that we can read information from files we would otherwise be oblivious to.

And let’s say that we have two available implementations of this FileParser, one dedicated to understanding XML (XmlFileParser) and one dedicated to understanding JSON (JsonFileParser).

So we’re done with our products description.

The client (our program) will need one of the two available implementations. To make the right choice, the logic is to test the filename and test its extension. If it’s “.xml”, use XmlFileParser, if it’s “.json”, use JsonFileParser. To avoid testing this everywhere in the code, we have decided to use the factory pattern that, thanks to its createFromFileName(fileName: String) method, will give us the correct FileParser…

…which is implemented at lightning speed thanks to Kotlin.

There we are — we have a to-the-letter implementation of a factory in Kotlin.

If we wanted to use the factory, we’d have written:

You said it would be fun…

Yes, I know what I said, so let’s keep diving into the subject.

What’s a bit boring with our factory is that we need to instantiate it. It’s not a lot of lines, but still. I’d love to be able to write something like

One line shorter … yes … it’s worth it!

The syntax looks like a static call, but as there is no “static stuff” in Kotlin, we’ll use a different trick — companion object. These objects are kinds of singletons attached to the class (see companion objects in the documentation).

Now we can use the previous syntax, and it’ll work. But the caveat is that we can no longer call the createFromFileName method from an instance of our FileParserFactory, only from the class itself with a static-like call.

And if you carefully read the code, you’ll see that our class doesn’t implement FileParserFactory, our companion object does.

Of course, we could write a sort of synonym for our instance to be able to expose such a method, but that would be ruining the concept. See for yourself:

If you’re asking yourself, “What’s wrong with having the static syntax as your only option?” I’ll answer with the simple, “It’s wrong because you can’t pass an instance of your factory to another object. Every client of the factory will have to be hardwired to the implementation of your CompanionObjectFactory.”

So … are we having fun yet? Just asking.

Er … Ok, let’s try another syntax.

If we still want to save a line of code while creating our FileParser, we can use another Kotlin feature, which is the object keyword. The object keyword is a way of asking your code to ensure that only one instance of a class will be available at any time in the life cycle of your application.

Let’s see how it’s done.

Yes, we’ve just replaced the class keyword by the object keyword.

From now on, we can use the one line option along with the possibility of calling the method from the instance itself.

I’m thrilled, my head is spinning right now…

Man you’re tough. Let’s try to spice things up then.

So, we have these FileParserFactories that handle the creation of FileParsers. That’s nice, but it would be nicer if we could embed the factory within the FileParser interface itself. Good news: in Kotlin, we can add a companion object to an interface.

Isn’t that fun? (At least for someone who comes from Java.)

Once again, we cannot give the factory instance to another object, but … is it still needed as the interface itself embeds its own factory? I agree that this very question is open to debate (because I do not fully agree with myself right now — I’m just getting carried away with the Kotlin syntax).

But if I need a different factory? How can I…

Hey! Excellent question.

The super fun thing with Kotlin is that you can add extensions to existing code. So if you need to write your specific factory, you can add it like this:

Yes, you’re “adding” a new method to the companion object within the interface, so from now on you will be able to call the new factory (fromFile) you’ve just declared.

More fun with a fun syntax

Imagine that you could write

Well, actually you can, by adding the keyword infix before your extension :-)

Infix means that you don’t need to call the method using the traditional syntax, you can call it the fancy way.

Well yeah … why not a factory method in the String itself?

You don’t say! Let’s do this :-)

So we can now create a FileParser using this syntax

That’s all the fun you’ll get for now

I’m sure there are many other ways to write crazy factories in Kotlin, but I was happy to share these ideas as I played with the language. If you want to tinker with the examples, you’ll find them in this GitHub repository.

Don’t hesitate to share your ideas (or your rants, as you wish!) in the comments.


Top comments (0)