As a general rule, developers should limit the scope of variables as much as possible. Keeping the notion of limiting scope in every way possible, from the big things (never, ever, ever use global variables)to the subtle little things a language can do (declare const parameters whenever possible) all can contribute to clean, easy to test code.
Limiting scope has all kind of benefits. First, limiting scope of a variable reduces the number of places that a given variable can be modified. The reason we rightfully abhor global variables is there is no telling where or when a given variable might be changed. A global variable hangs out there like that last donut in the break room. You know you shouldn’t touch it, but oh my, it’s so tempting, and it’s so easy to just grab that variable and use it for whatever you need to do. Limiting the scope of a changeable thing reduces the chances that it will be changed in a way that it shouldn’t be, or worse – in a way that you don’t even know about.
Limiting scope is also, by definition, a main feature of object oriented programming. Encapsulation is probably the first thing you thing of when you thing of OOP, and it’s basic purpose – it’s whole reason for existing, really – is to formalize the process of limiting scope. They don’t call it “information hiding” for nothing. The term “information hiding” really means “Hide the scope of your variables so much that they can’t be changed except in the exact way you let them be”. Encapsulation enables you to ensure that data you don’t want exposed isn’t exposed. And if it isn’t exposed, it can’t be changed.
Limiting scope also limits the ability for people to depend on code that they shouldn’t be depending on. Loose coupling – the limiting of the dependencies with in a system – is a good thing. Tight coupling is a bad thing. Limiting scope makes it harder for one class to latch on to another class and create dependencies that make code hard to test and hard to maintain. It also makes it harder for changes in one area of code to have secondary effects in other, coupled areas. There is nothing worse than changing code in (metaphorically speaking) New York City, and having that change cause a bug in code all the way over in Los Angeles. (This is commonly called “Action at a Distance” and is something that should be avoided like a bad burrito.)
There are big, well known ways of limiting scope – objects, making a variable local to a method, etc. – but there are a lot of smaller, more subtle ways to limit scope as well. I’ve tried to list a number of ways that you can limit scope in your Delphi code:
- No global variables. None. Zero. -- This one isn’t so subtle, but it’s really, really important. It’s really easy to make sure that you never ever declare a variable as global. In Delphi, you can do it, but you never should. In languages like C# and Java, you actually can’t, strictly speaking, declare a global variable – everything has to be in a class -- but you can create a field on your main class that is pretty much the same thing. Delphi is more flexible and will let you declare a global variable. But just because you can doesn’t mean you should. So don’t.
- Don’t create anything. Instead, ask for what you need. This topic is a big one unto itself and gets into the question of dependencies and dependency injection. But if you try to never actually create anything in the constructor of your class and instead, pass the constructor existing instances of what it needs, you can limit the scope of a given class. Better yet, if you use a full blown dependency injection container, you can very severely limit the scope of your classes and still benefit fully from their functionality.
- Put everything you can in the implementation section of your units. The implementation section is the friend of a Delphi developer trying to limit scope. Things in an implementation section can’t be seen outside the unit. If you can put a unit name in the implementation uses clause, do it. If you can declare a class definition in the implementation section, do it. Putting something in the implementation section of a unit ensures that it scope doesn’t expand beyond that implementation section.
- If a variable can be local to a routine, do it. This goes along with “No global variables ever”, but it needs to be said. If you can declare a variable in the scope of a single method, do it.
- Use constants whenever possible. If you are never going to change the value you assign to a variable, why make it a variable at all? Get in the habit of making something a constant, and then only making it a variable if it needs to be. Do this even inside your methods.
- If you can declare a parameter as const, do it. A const parameter can’t be changed, and thus declaring it as such means you are limiting its scope.
- If you can make a variable private, do it. If you can’t, make it protected. If you can’t do that, make it public. Only make something published if it has a very specific reason to be. This one is a little trickier, because there are OOP principles to be considered. But generally, make field variables private, and give access to them through protected members. This way, while the data can be changed, the actual storage device itself is not directly accessible. In addition, you can control the ability to change the internal data through a setter method.
- Provide notification systems in your setters to monitor changing data. Sometimes, you might have data that needs to be changeable, but is sensitive for some reason. As as result, you want to perhaps monitor and maybe even prevent the changing of your internal data. You can use something as simple as an event in the setter method, or something more robust and flexible as the Observer pattern.
- Don’t reach into another class. Or even better, don’t create classes into which someone else can reach. This gets into the Law of Demeter, which I’ve discussed before.
That’s just a list I came up with this week while working on this post and thinking about this topic. What are other little methods and techniques that you all use to limit the scope of your variables?