‘Deficient Encapsulation’ Smell in java.lang.System Class

‘Deficient Encapsulation’ smell occurs when the declared accessibility of one or more members of an abstraction is more permissive than actually required. For example, a class that makes its fields public suffers from Deficient Encapsulation.

An extreme form of Deficient Encapsulation smell occurs when there exists global state (in the form of global variables, global data structures, etc.) that is accessible to all abstractions throughout the software system.

Consider the following fields defined in java.lang.System class:

public final static InputStream in = null;                                                           public final static PrintStream out = null;                                                         public final static PrintStream err = null;

These members are declared final, but they can be “reset” using methods setIn, setOut, and setErr, respectively (these methods internally make use of native methods to reset the final stream variables) provided in java.lang.System class. For instance, here is the declaration of the setOut method provided in java.lang.System class:

public static void setOut(PrintStream out)

Since these fields are declared public, any code (i.e., other classes in JDK as well as application code) can access these fields directly. Hence, these fields are effectively global variables!

Common reason for making such fields public instead of providing getter methods is performance: since these fields are quite extensively used, it could considered as an unnecessary overhead to provide getter methods. However, providing such direct global access to fields is problematic. For illustration, let us consider one such problem. The fields out and err are instances of type PrintStream. The PrintStream class was introduced in Java version 1.0 and supported only 8-bit ASCII values. Hence, to support Unicode, JDK 1.1.5 introduced PrintWriter class as a replacement to PrintStream class, which they intended to deprecate. However, fields of PrintStream type such as System.out and System.err are directly used by most of the Java applications to access PrintStream’s methods.

For instance, every time we use System.out.println, we access the println method defined in PrintStream class! Hence, it is not possible to deprecate the entire PrintStream class!

In summary, the deprecation of the PrintStream class was made difficult because of the Deficient Encapsulation smell in the System class which publicly exposed the data members out and err of type PrintStream.

Refactoring for this smell

To refactor this smell in java.lang.System class, one or more abstractions could be introduced to provide the functionality provided through the public members. For example, a class named, say, Console, could abstract the functionality that is provided by standard input, output, and error streams. This Console class could hide the implementation details (such as specific Stream classes used, kind of synchronization performed, etc.). Such a design would allow for changes in the implementation inside the Console abstraction, unlike the case in which data members (such as out and err) are directly exposed to the clients.

In fact Java 1.6 version introduced java.io.Console class that provides “methods to access the character-based console device.” One can retrieve Writer or Reader objects associated with the Console object using reader() and writer() methods.

The class also provides methods such as readLine() and printf() for reading and writing strings to console, respectively. The readers and writers supported by this Console class wrap the actual input and output streams on which they operate. The Console class also uses locks internally to synchronize the reads and writes. In short, the Console class encapsulates the operations on input and output streams. This class is perhaps not meant to be a replacement of the original standard input, output, and error streams; however, it does illustrate what alternative design approach could be used instead of exposing streams directly to clients.

[Source: “Refactoring for Software Design Smells: Managing Technical Debt”, Girish Suryanarayana, Ganesh Samarthyam, Tushar Sharma,ISBN – 978-0128013977, Morgan Kaufmann/Elsevier,2014. http://amzn.com/0128013974]

Leave a Reply

Your email address will not be published. Required fields are marked *