DEV Community

geraldcroes
geraldcroes

Posted on • Originally published at Medium on

3

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.

interface FileParser
view raw FileParser.kt hosted with ❤ by GitHub

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).

class XmlFileParser : FileParser
class JsonFileParser : FileParser
view raw FileParsers.kt hosted with ❤ by GitHub

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.

/* The concept of the factory -> creates a Product */
interface FileParserFactory {
fun createFromFileName(fileName: String): FileParser
}
/* Our specific Factory */
class StandardFileParserFactory : FileParserFactory {
override fun createFromFileName(fileName: String) =
when (fileName.substringAfterLast('.')) {
"xml" -> XmlFileParser()
"json" -> JsonFileParser()
else -> throw Exception("I don't know how to deal with $fileName.")
}
}

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:

val parserFactory = StandardFileParserFactory()
val fileParser = parserFactory.createFromFileName(“filename.xml”)
view raw client.kt hosted with ❤ by GitHub

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

MyFileParserFactory.createFromFileName("filename.json")
view raw client.kt hosted with ❤ by GitHub

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).

class CompanionObjectFileParserFactory {
companion object : FileParserFactory{
override fun createFromFileName(fileName: String) =
when (fileName.substringAfterLast('.')) {
"xml" -> XmlFileParser()
"json" -> JsonFileParser()
else -> throw Exception("I don't know how to deal with $fileName.")
}
}
}

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:

class CompanionObjectFileParserFactory : FileParserFactory {
companion object : FileParserFactory{
override fun createFromFileName(fileName: String) =
when (fileName.substringAfterLast('.')) {
"xml" -> XmlFileParser()
"json" -> JsonFileParser()
else -> throw Exception("I don't know how to deal with $fileName.")
}
}
/* needed if you want to be able to call the createFromFileName from an instance */
override fun createFromFileName(fileName: String) =
CompanionObjectFileParserFactory.createFromFileName(fileName)
}

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.

object ObjectFileParserFactory : FileParserFactory {
override fun createFromFileName(fileName: String) =
when (fileName.substringAfterLast('.')) {
"xml" -> XmlFileParser()
"json" -> JsonFileParser()
else -> throw Exception("I don't know how to deal with $fileName.")
}
}

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.

//Works…
ObjectFileParserFactory.createFromFileName("filename.foo")
//...and this still works
val fileParser = ObjectFileParserFactory
fileParser.createFromFileName("filename.json")
view raw client.kt hosted with ❤ by GitHub

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.

interface FileParser {
companion object {
fun createFromFileName(fileName: String) =
when (fileName.substringAfterLast('.')) {
"xml" -> XmlFileParser()
"json" -> JsonFileParser()
else -> throw Exception("I don't know how to deal with $fileName.")
}
}
}
view raw FileParser.kt hosted with ❤ by GitHub

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

FileParser.createFromFile("filename.json")
view raw client.kt hosted with ❤ by GitHub

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:

fun FileParser.Companion.fromFile(filename: String) =
this.createFromFileName(filename)
view raw extension.kt hosted with ❤ by GitHub

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

val parser = FileParser fromFile "filename.json"
view raw client.kt hosted with ❤ by GitHub

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

infix fun FileParser.Companion.fromFile(filename: String) =
this.createFromFileName(filename)
view raw extension.kt hosted with ❤ by GitHub

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 :-)

fun String.createFileParser() =
when (this.substringAfterLast('.')) {
"xml" -> XmlFileParser()
"json" -> JsonFileParser()
else -> throw Exception("I don't know how to deal with $this.")
}
view raw extension.kt hosted with ❤ by GitHub

So we can now create a FileParser using this syntax

"filename.json".createFileParser()
view raw client.kt hosted with ❤ by GitHub

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.


Sentry mobile image

Is your mobile app slow? Improve performance with these key strategies.

Improve performance with key strategies like TTID/TTFD & app start analysis.

Read the blog post

Top comments (0)

The Most Contextual AI Development Assistant

Pieces.app image

Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.

👥 Ideal for solo developers, teams, and cross-company projects

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay