One of the important aspects of software development is maintenance, and it's proven by experience that software that keeps visibility of its component low is more maintainable than the one that 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 a must-have requirement for any 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 the visibility of any Type like 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 modifiers with types, protected is not a legal modifier for class and interface.
Also, a top level class (a class whose name is the 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.
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.
A 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 a nice way to understand the need for controlling the visibility of class and interface. Another interesting case is a component-based Java application server like JBoss, WebLogic, or WebSphere. This server provides low-level services e.g. transaction management, security, persistence, object pooling, etc.
In short, a production system uses both the application server's code as well as the application's code to work perfectly. In order to be maintainable e.g. switching between a different application server, your app, and server code should be loosely coupled and should maintain a safe distance.
Application server's internal implementation classes and interfaces should be completely hidden from the user applications for security purposes. 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.
It provides three ways to control the visibility of any Type like 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 modifiers with types, protected is not a legal modifier for class and interface.
Also, a top level class (a class whose name is the 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.
A 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 a nice way to understand the need for controlling the visibility of class and interface. Another interesting case is a component-based Java application server like JBoss, WebLogic, or WebSphere. This server provides low-level services e.g. transaction management, security, persistence, object pooling, etc.
In short, a production system uses both the application server's code as well as the application's code to work perfectly. In order to be maintainable e.g. switching between a different application server, your app, and server code should be loosely coupled and should maintain a safe distance.
Application server's internal implementation classes and interfaces should be completely hidden from the user applications for security purposes. 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 favorite EnumSet class. Java designer made it abstract class to avoid instantiation, and provided factory methods as the only way to create an instance of that class e.g. EnumSet.of() or EnumSet.noneOf() methods.Internally they have two separate implementations in the form of RegularEnumSet and JumboEnumSet, which is automatically chosen by static factory methods depending upon size of key universe.
For example, if a 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 this implementation are package-private means clients 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 the visibility of classes from JDK itself.
That's all about why you should control the visibility of your class and interface and How to do that in Java. In short, minimizing visibility also leverages the 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.
For example, if a 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 this implementation are package-private means clients 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 the visibility of classes from JDK itself.
That's all about why you should control the visibility of your class and interface and How to do that in Java. In short, minimizing visibility also leverages the 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 :
Actually, nested classes and interfaces can be protected.
Post a Comment