Okay, so the whole FreeAndNil thing has been going on for a while. Some folks are understandably sick of it, but I’m not. I think it is an important discussion that actually reveals a much deeper issue – sort of like how a fight with your spouse over the toothpaste tube is usually really a fight over something deeper and more serious.
I think in this case, the “fight” (I don’t like that word for this particular case, but we’ll roll with the metaphor…) is really about the approach one takes towards memory management – specifically, how one views the role of class creation (and thus memory allocation) when writing code. In my Dependency Injection Series (which I really need to continue…), I spoke about the notion of creating classes without constructors (other than the default one, of course). In a sense, this post might be considered part of my DI series, because in this post I am basically making the case for using a Dependency Injection container. In this post, I’m going to argue that for the most part, you should not call Create. And you should design your code in such a way that you don’t need to call Create.
Life Too Short?
Okay, so I was watching a terrific video by Neal Ford (a former Delphi guy, actually – ) in which he introduces the notions of and “way of thinking” behind Functional Programming. It’s a great video, and you should watch it. Near the end of it, Neal says something that I’ve been pondering a while: “Life is to short to call malloc”. He doesn’t think he should have to worry about memory management anymore. This somewhat provocative thought stream-lined with a notion I ran across as I was investigating and thinking about unit testing – the idea from Misko Hevery where he is deeply suspicious of the new operator, Java’s equivalent of .Create. At first, this seems like a really weird notion, particularly since Delphi is a native language, and many Delphi developers appreciate the ability to control the lifetime of their objects and directly manage memory . But I started to see the wisdom in it, and have finally come to agree with Neal – Life is too short to call .Create. I don’t want to be spending my limited thinking and coding time worrying about memory, particularly since there are now frameworks and language features that can do this automatically. In languages like Java and C# (among others), developers use garbage collection to manage memory. We don’t have garbage collection in Delphi, but we do have ways of making memory management something that isn’t a huge concern.
And hey, if you are a Delphi developer, and you want to completely control your objects lifetime manually, they hey, you can. More power to you. But again, I’m going to show you how you can spend less time worrying about that and more time doing the things that we developers really are trying to do -- which is come up with solutions and products.
This isn’t the first time that I’ve thought or mentioned all of this stuff:
First, I am not the sharpest knife in the drawer and I have a limited number of CPU cycles in my brain. If I don’t have to, I don’t want to spend them doing repetitive “busy work”. And calling the Create/Free cycle is, to a large degree, busy work. We all do it – we carefully ensure that if we call Create, we code the try…finally block and call .Free. If we create an instance class variable in a constructor, we immediately call .Free in the destructor. Those are all good practices, and bravo to you for following them, but I think we’ve now gotten to the point where we don’t always have to do these kinds of tedious, wrote coding activities anymore. If we can spend time thinking more about higher level solutions and less about lower-level intricacies, we are more productive. (One could argue that the entire field of computer science has been nothing other than a giant march towards spending time on higher level solutions and away from tedious, low-level coding…)
Don’t Cause Dependencies
But that is more of a basic, low-level reason in itself -- the idea that you can save brain power. There are some deeper technical and practical reasons why you should eschew creating anything. The first is that calling .Create creates more than just an object instance -- it creates a dependency. If you actually create a class instance your self and use that class via a direct class reference, that is, do something like this:
SomeSprocket := TSprocket.Create
you are dependent on that class and that class alone. You’ve created an unbreakable dependency on that particular implementation. You have to include that classes unit in your uses clause. You’ve very tightly coupled yourself to a specific means of getting something done. If you want to change it, you actually have to go and alter the code itself. Yikes! If you want to test it, your tests have to rely on the entire dependency chain of TSprocket, and who knows what the heck that entails? And I hope we can all agree that tightly coupling yourself to specific implementations is bad, right? (If we can’t, I’m afraid that we can’t be friends anymore. Alas.)
As I’ve been preaching for a while now, you should reference classes and functionality via interfaces. Interfaces allow you to, at the very least, reference specific a functionality rather than a specific implementation of that functionality. But even in you use interfaces exclusively -- that is do something like the below instead of the above --
SomeSprocket := TSprocket.Create
you are still creating a dependency on that particular class. Your uses clause still has to include the unit for that class, and your code is explicitly tied to that particular implementation. Using the interface is a step in the right direction – using an interface makes you not have to worry about memory management – but you still end up with dependency on a specific implementation. And we agree that a dependency on a specific implementation should be avoided, right?
So, we agree that calling Create causes a hard-coded dependency and that it is bad. And if you think about it, anytime you call create in one class, you create a dependency. One reason such dependencies are bad is that it makes your code hard to test. True and proper unit testing means that each class should be able to be tested in isolation. If ClassA is irrevocably dependent on ClassB, then you cannot test ClassA without invoking ClassB. You can’t easily provide a mock class for ClassB either. Thus, a call to Create can make your code hard to test.
But there’s yet another reason to avoid calling Create – the ‘S’ in the SOLID principles. The ‘S’ stands for the “Single Responsibility Principle” which is “the notion that an object should have only a single responsibility.” If your class has a mission and it is creating stuff to do that mission, then it actually has multiple responsibilities – doing its mission and creating stuff. That’s two things, not one. Instead, let your class do it’s main mission, and leave creating stuff up to another class whose main mission it is to create stuff. Plus, if you have dug into the SOLID principles, you know that the ‘D’ stands for the “Dependency Inversion Principle” which is the notion that “one should depend upon abstractions [and] not depend upon concretions.” Or, as you might have heard me say say “Program against abstractions, not against implementations”. (And of course, it wasn’t me that said that – it was Erich Gamma of “Gang of Four ” fame.)
And where does that leave us? Well, right back with not calling Create, and having a class whose specific, single purpose in life is to create stuff for you. Sound familiar? It should, we just described either a class based on the Factory pattern, or a Dependency Injection container.
So, in the end, the code you’d write would end up looking like:
SomeSprocket := SprocketFactory.GetSprocket;
// Or maybe something like
// SomeSprocket := MyDIContainer.GetImplementation<ISprocket>('Basic');
That way, there is no Create call, and thus you aren’t creating any dependency other than on the interface. You get instances of your objects from a Factory or from a Dependency Injection container -- and either one can produce any implementation you want or ask for without creating a dependency. Either way, it gives you control over how the interface is implemented without coupling to that implementation. You could even add a parameter to the GetSprocket call to ask for a specific kind of Sprocket implementation, and even that wouldn’t cause you to be dependent on that implementation.
In the end, calling Create merely causes a dependency, takes brain cycles, and violates the SOLID principle. No wonder you shouldn’t use it much!
So then the question becomes, When should you call Create? Well, I’m glad you asked. I’ll answer that in a future post.