Friday 19 December 2008

Item13: Minimize the accessibility of classes and members

This is the first item in the Chapter about Classes and Interfaces. It deals with the first of the fundamental principles of object oriented design: information hiding or encapsulation. This concept is already well known and described in Parnas famous ACM article. All OO languages have support for that, so does Java.

Reasons for information hiding should be obvious:
  • it enables to decouple modules and hence the system can get a better
    internal software quality
    (in terms of developing, testing, understanding, maintaining, etc).
  • it also enables or increases software reuse
    (though we should have a look at this, so that we do not copy and past
    modules, etc).
Java supports information hiding via the access control mechanism (JLS
6.6) (private, package private, protected, public) for classes, interfaces and members (methods and attributes). The rule of thumb is simple:
make the artefacts as inaccessible as possible (so make them private if possible).

In Java you have two options: For classes and interfaces you can have package-private and and public. For members (fields, methods, nested classes and nested interfaces) you can have private, package-private, protected, public. The default (if you write nothing) is always package-private.

Note: In Effective C++ Scott Meyers as the same rule (3rd edition, Item 22). The interesting thing Scott discusses there: protected is not better than public (at least for C++, because there is no final clause to prohibit inheritance)

Usually a good API design, should use as much private as possible gain
the Parnas properties we described above. However caveat: I you implement Serializable, also private and package-private fields, leak into the public API,
which is not what the designer/programmer intended (Item 74/75 describe this in more detail).

Note also: that even private and package-private members are accessible via reflection from arbitrary classes. So if a programmer intends to violate the information hiding rules, he CAN do this in java via some reflection tricks.
Note that a similar problem is also there in C++, e.g. if you pass out the pointer to a private member via a public method, you loose all the encapsulation guaranteed of the language.

Josh continues discussing rules about backward compatibility and some minor issues with "trying to change the access level".

A good collection about backward compatibility rules I found really really useful is that of eclipse. (URL below).
It is really worth reading this rule collection: !
http://wiki.eclipse.org/index.php/Evolving_Java-based_APIs
http://wiki.eclipse.org/Evolving_Java-based_APIs_2
http://wiki.eclipse.org/Evolving_Java-based_APIs_3

Of course, classes with public mutable fields are not thread-safe, so
watch out for this and avoid it like a plague. The only thing you should have are public static final fields, containing constants for this class, written in UPPER_CASE_NOTATION. Note however that these public static final fields should ONLY be primitive values or references to immutable objects. If you have a final field with a reference to a mutable object or a static final array field, you are lying at yourself. You might feed better in the first place but such fields are still mutable, what you really have is a serious security hole .

So the basic rules for me to remember are:
  • Strive for the complete and minimal public API of a class/interface (Scott Meyers has the same rule...)
  • Prevent any access from outside that is not necessary.
  • ensure that public static final fields are REALLY immutable
  • if API backward compatibility matters, obey the Eclipse Java based API Evolution rules (links are in this article)

No comments: