I am not talking about project scope and “scope creep” or “ambiguous scope” that we all deal with from project to project. Rather, I am referring to the scoping of our classes and data, and the little decisions we make, or fail to make, in regards to what scope our classes and data will have. In general we have 4 choices: public, internal (package in Java, friend in VB), protected or private. For each class we design, each property we assign to it, and each method we assign to the class we make a decision as to its scope. As OO developers who value encapsulation and low coupling we would like to keep everything private as the broader the scope the less control we have over what is exposed.
There are multiple factors at play that affect our scope decisions. I aim to focus on just two that I feel are the most important and that are sometimes confused or even ignored. Number one is simply encapsulation and the second important factor is levels of abstraction. When we don’t acknowledge them both we end up with designs that can be confusing, overly complex to deal with, and harder to maintain and extend.
Encapsulation is often referred to as the cornerstone of the object-oriented paradigm. Keeping data and behavior together is paramount to achieving the benefits of an OO design. In an ideal OO world all the data within a class would be private and all the code that uses the data would be contained in the class itself. Back in the real world and our common business application, we know this is not always possible. We have to acknowledge the benefit of loose coupling and the need for internal scope. With internal scope we at least know that any changes will only affect the project or package we are working with. Thus we can refactor, extend, and alter code with a certain level of confidence since we know we can easily compile and test that code. However, we now don’t know who might be using our class’ data within the library. Now other coders (including ourselves) are susceptible to rewriting the same logic that uses some exposed data. Furthermore, exposed data lends itself to writing procedural code that makes decisions based on the data: object-based development. In other words, we have lost some control, and now maintenance and extension just became a little harder. Our final option if we really deem it necessary is to publicly expose our data. This is often common when we want to display an object on a user screen or use its data in a report. This is a necessary evil. Now changing a class’s data could affect any and every project using our library. Our changes could possibly cause the recompiling of all our projects and possibly break one or more of them. We have now lost any control of our data, and maintenance and extension can become unbearable if too much is made public.
The second factor that affects the way we scope things the way we do is levels of abstraction. We want to be able to view our system at the highest level with just a few things to comprehend. We can then dig deeper if necessary to see more details behind what is going on. The public application services, public classes, public class properties, and public methods at the highest level all match up with the language of our problem domain. If a domain expert were giving a presentation of the new system, the nouns and verbs they spoke of would (should) match the public interface to our design. We should make an effort that only these high-level classes and their high-level methods and data be exposed publicly so we can preserve this level of abstraction. This provides an easy to understand need-to-know level of abstraction. So aside from making scope choices based on just encapsulation we should concern our selves with what concepts really need to be exposed publicly.
The truth is that the big picture view of a system does not change as much as the finer details. Sticking with a solid public interface to your system that matches the big picture view allows you greater flexibility in working with the details without disrupting the public interface. This is especially true if you do iterative design with refactoring where you often clean up details and spruce up the implementation behind the scenes as you go. When you scope your classes, properties and methods with both encapsulation and levels of abstraction in mind you end up with code that is much easier to extend and maintain and a domain model that is easier to understand.