Separate Use from Creation

In object-oriented programming, there’s the creation of the object—we call it instantiation—and then there’s the usage of the object. It makes a lot of sense to separate out those two phases so that one group of objects are only dealing with creating objects and another group of objects are only dealing with using objects. The objects that create objects are called factories. Factories simply encapsulate the new keyword. The new keyword is the way we create runnable objects from their class definitions. new has a requirement, which is that it has to know what is newing up.

Suppose I said to you, “I would like you to do something for me.”

And you reply, “Fine, what?”

Then I say, “Something. Now do it.”

You’re probably going to laugh at me.

Don’t do that in code. In code you want to be able to create something. When you new up an object, it requires that you pass in the type of object you’re newing. It’s an absolute requirement. Just like if I ask you to do something for me, you have to be told what it is I want you to do or you can’t do it. It’s not new’s fault if it doesn’t know what to do without being told.

But one of the techniques used a lot in OO is called polymorphism. It’s a big word but a simple concept: We want to be able to do a task but not have to worry about the details of how the task is accomplished.

For instance, say I want to decompress a file with the corresponding decompressor that was used to compress it. If it was zipped, I want to unzip it. If it was packed I want to unpack it. But maybe I don’t care about that distinction. I just know that I have a file and it was compressed in some way and I want to decompress it in any way that’ll get the job done.

Now, that’s almost as hard for a computer as me saying to you, “Do something for me,” and not telling you what. The computer needs to know exactly what decompression routine to follow. So I can have another part of my system figure that out. In this case, the object that would figure out which decompressor to use would have access to the object that was compressed because that object knows how it was compressed. It shouldn’t be the responsibility of the client code of the other program to figure out how it was compressed. The client code should be able to say “decompress yourself” and have the right decompressor applied as if by magic…but it’s not really magic.

That’s what polymorphism does. It allows you to build blocks of code independent of each other so they can grow independently from each other. For example, when someone comes up with a new compressor that was never envisioned before, the existing code can automatically take advantage of it because it’s not responsible for selecting the compressor to use. It’s just responsible for delegating to the compressor it’s given. As long as it can handle the job it’s supposed to, everybody’s happy and the code works. That technique is great for making your code independent from other code in the system and allowing you to extend a system in a safe and effective manner.

But in order to do that correctly you need to create objects separately, in a different entity than the entity that’s using the object. By isolating object creation we also isolate the knowledge about which concrete objects are being used and hide it from other parts of the system.

Not only does this decouple business rules from their actions, but it also decouples the dependencies between the caller and the services they use. You can then mock those services and isolate your code from the services they use so they can be tested in isolation.

Separating creation of objects from their usage lets you break dependencies to improve testability. It decouples code so it’s easier to modify and maintain, and it simplifies code because factories only create objects but never use them so they’re easy to test—just pass in your business rules and see what objects the factory returns. And this approach also simplifies our usage code because it has fewer conditional statements and doesn’t have to be concerned with instantiating objects.

Of course, if I need to use a feature of the language or framework I’m using then I’ll just new up the object inline. But for my own classes that I might want to extend in the future, or for external dependencies that I’ll definitely want to mock out for testing, I’ll make sure another entity instantiates and passes in a service object that another object uses.