Object Oriented Programming, part 1 - Scope
For a while now I have been noticing people saying that they are doing OOP but instead they are actually programming in procedural style. I am going to start series of posts about OOP to clarify and explain Object Oriented Programming. Of course this may not be the most comprehensible guide but I hope this will help some of us to grasp basic concepts (but not intro about syntax and basic terms). I am going to assume that one knows what class, object and method is and has already done some programming with these in his/her favourite programming language
Lets start our little talk about Object Oriented Programming about defining goals that we hope to achieve by making some software. Our initial goal is to complete something that would help us in some way (faster calculations, work automation and etc. ). It would be reasonable to add goal “with the least effort”. So now we have formulation as this: goal is to have something that helps us with the least effort as possible.
I doubt this could be held as complete programming “uber-rule” but it makes sense :-). Now lets get back to OOP. I am going to assume that one knows what procedural programming means and how it compares to OOP. OK… short overview (from my Ivory tower): “steps to manipulate data to achieve desired result”, while OOP would stand for something like this: “ Defined object interactions to achieve desired result”.
The rest of this part will focus what does “Defined objects” and “interactions” means and how it helps us to achieve our defined goal of making working stuff fast. I think this is needed to fully understand OOP and stop making procedural code in OOP (and again - we are doing this to make our software development process less expensive)
Most of us are already aware that most of software development time goes to maintenance of existing systems instead of actually developing new ones (the rough estimate is that 25% of time goes to making new features and 75% to tinkering with existing code) [ref]. We can see that by reducing our effort on those 75% would make most sense. This goes along our goal of making software with the least effort. So how does OOP help here? Also, I would like to quote Martin Fowler on this matter: ”Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” Writing initial code is easy. It is hard to write code that is easy for others to maintain :).
So lets begin. First thing that OOP helps you with is scope handling. Eventually you will be actually manipulating data but scope handling is going to make this easier by reducing the amount of stuff you have to understand before making any modifications. The first place where you start handling your complexity by scoping are classes. This is layer that doesn’t exist in procedural code. This happens when you define fields inside class. Preferably you are going to want to make ‘em private or at least protected but definitely not public. By doing so, you are reducing number of places where this data could be manipulated only to instances of single class. To keep this “feature” at the level that you are gaining benefits you may want to distribute that data among different classes that handles those specific cases. Failing to do so means you are going to end up with god-class anti-pattern and your code is going to be just plain procedural one but encapsulated in single class.
Another way of scope handling is apparent when you are defining methods in classes. Keeping methods distributed among classes helps you handle complexity by explicitly showing what data can method manipulate because it can access data that is only available to that class. Again, this holds true only when your methods are using data from your class instance and if your methods just take data as arguments and manipulate it you are again just using procedural programming and loosing scope handling mechanisms that OOP provides. Perfect examples in such worrisome cases would be public static methods or public methods that don’t involve current instance state (this case probably means you should apply Move Method refactoring).
Almost in all cases when you are using OOP you have defined lots of different classes and pass instances of those classes all around. How does this help you? Instances carry explicit information about what can be done with them while plain data doesn’t. This is done by method signatures - you can’t call method that doesn’t exist in the object and when you call it you can make assumption that it does correct things (unless you don’t have faith in your fellow programmers). Using plain data means that before you can do anything meaningful with it you must understand its format first. This is especially apparent when you are using strongly and statically typed languages that can tell you if you are doing something wrong during compilation phase.
Another useful feature, at least in Java, is access modifiers “public”, “private”, “protected” and “default” (yes - there is such and I am disappointed how heavily it is underused). They give you opportunity to define explicitly scope that data and methods should be exposed to and how they were intended to be used. Almost all data should be “private” to make most of the use of method signatures (methods should change state by calling them by their explicit signature instead of modifying some generic underlying data formats - doing so reveals early misuse of code). Methods could be made public, private, protected or default. I find it a rare case to make method protected as I think it always leads to some kind of misuse and anti-patterns such as Yo-yo problem. Private could be used to show that this peace of code (method) exists only to be called from class itself and there should be no reason to call it from outside. Public shows that this method could be used to do something meaningful for others. And lets leave “default” for the next section….
Also, there are packages and “default” scope features in Java (I bet C# has it too) . This can be used as one more additional layer of scoping on top of classes to group and isolate classes from external “disruptions”. “Default” scope inside of package means that classes can communicate freely between packages but calls to these classes outside of package is forbidden. This helps to isolate classes that solves sub-problems for our given problem and show our interfaces through some single class that exists as entry point.
To wrap all this nasty stuff up and to clarify things: all these mentioned ways of scoping helps to deal with code complexity by hiding it. We should hide as much as we can and just leave only very necessary stuff exposed. By hiding various parts you enforce some specific ways to use your code as you intended thus helping other developers to use it correctly and lessen amount of mistakes. Lower mistake count means higher productivity.
I think that is all that I wanted to say in this technical part. I am now writing continuation to this about OOP aspect of “carrying a meaning”. That follows closely to these principles mentioned here. Only after reading both of these parts you should have consistent holistic view about OOP basics.
You may notice that by abandoning simple rules following from these principles often leads to common anti-patterns and unmaintainable code. Such as methods that don’t make use of current instance state often means that they are in wrong place [ref] or overuse of public access modifiers means spaghetti code. I strongly advice you read about Refactoring. This book by Fowler would provide very strong foundation about it and here is catalog of its contents online. I will expand on this topic some time later.
Wait for the next part…