Why you should control Visibility of Class and Interface in Java

One of the important aspect of software development is maintenance, and  it's proven by experience that a software which keeps visibility of its component low is more maintainable than the one who exposes its component more. You won't realize it upfront, but you will miss it badly, while redesigning your application. Since maintaining backward compatibility is must have requirement for many app, you end up patching and repeating same mistakes. You can not do much because lots of other applications are tightly integrated with your class and interfaces. Java has always put encapsulation on priority, provided support of access modifiers from very beginning. It provides three ways to control visibility of any Type e.g. class or interface, by making them public, package-private or private. What happened to protected, can't we use protected with class or interface. No you can't, you can only use two access modifier with types, protected is not a legal modifier for class and interface.

Also a top level class (a class whose name is same as of Java source file which contains it)  can be either public or package private (without any access modifier), it can not be private. Only a nested class can be private, public or package-private.

A public class is accessible to everyone, and it is most visible, try to keep only key interfaces public, never let your implementation go public until you think it's complete and mature.

On the other hand private Type is least visible, and only nested class or interface can be private in Java. Since it's least visible, you have full control of this class to alter its behaviour with experiences, new technologies, tools and redesign.

A clever midway is package-private visibility, which is also default visibility, there is no such keyword as package-private, instead if you don't provide any access modifier than Java assumes that it package-private, and subsequently make it visible only on same package.

If your classes and interfaces are shared only between other class in same package, make them package-private. Since client cannot access them, they are also relative safe to change.

How to control Visibility of Class or Interface in Java

Apart from reducing visibility of class or interface using access modifier, there are couple of other ways to do that, depending upon your runtime environment as well. At component level, such as in Application Server like Websphere, Weblogic or JBoss, an implementation class can be proxied or wrapped to minimize external exposure.

No matter what you do, there will always be some types, which needs to be exposed to external world, but with proxy or wrapper, you can still manage them. Even though client programs, can load proxied implementation class, they will mostly get an immutable proxy or wrapper.

For example getServletContext() from Java Servlet API (javax.servlet) returns an implementation of javax.servlet.ServletContext, which is usually an immutable proxy to fulfil promises made in ServletContext interface. It's most likely that application server is running with different implementation of javax.servlet.ServletContext interface.

Similar pattern can be used in the implementation of other externally exposed interfaces e.g. ServletRequest, ServletResponse, javax.ejb.EJBContext, javax.ejb.TimerService etc. Different application servers may use different implementation to support these global interfaces.

Writing open source libraries is also nice way to understand need of controlling visibility of class and interface. Another interesting case is component based Java application server e.g. JBoss, WebLogic or WebSphere. This servers provides low level services e.g. transaction management, security, persistence, object pooling etc. In short a production system uses both application server's code as well as application's code to work perfectly. In order to be maintainable e.g. switching between different application server, your app and server code should be loosely coupled and should maintain safe distance.  Application server's internal implementation classes and interfaces should be completely hidden from the user applications for security purpose. If the application packages the same library that the server contains, care must be taken that the server does not inadvertently load the application's version via thread context classloader.

JDK Example of Controlling Visibility of Java Class

One more interesting example of controlling visibility is my favourite EnumSet class. Java designer made it abstract class to avoid instantiation, and provided factory methods as only way to create instance of that class e.g. EnumSet.of() or EnumSet.noneOf() methods. Internally they have two separate implementation in form of RegularEnumSet and JumboEnumSet, which is automatically chosen by static factory methods depending upon size of key universe. For example, if number of values in given Enum is less than 64, then RegularEnumSet is used, otherwise instance of JumboEnumSet is returned. Beauty of this design is that, both of these implementation are package-private means client have no idea about them. They are completely transparent to users and there is additional security enforced by making these class abstract, because you cannot create instance of abstract class. This not only allow you to choose most appropriate implementation but also it would be very easy to replace them with newer and better implementation. Though they are really special class, and RegularEnumSet uses a long value to store enum constants. IMHO, this is one of the fantastic example of controlling visibility of classes from JDK itself.

That's all about why you should control visibility of your class and interface and How to do that in Java. In short, minimizing visibility  also leverage benefit of Encapsulation, a well encapsulated code is more secure and maintainable. With the pace of technology, whatever you write today, becomes outdated in couple of years, following basic principles of class design can help you get most from updated tools, libraries and JDK implementation.

1 comment :

Arend v. Reinersdorff said...

Actually, nested classes and interfaces can be protected.

Post a Comment