ChiMu Publications Java Development Standards | Previous TOC Next |
Interfaces are the primary mechanism that developers should be using to internally and externally couple their software: typing of variables, parameters, return values and casts should be primarily done using interfaces and not classes. Classes are implementations of interfaces and should provide no public behavior beyond the interface itself (other than how to create and initialize an object of that class). Interfaces are given no suffixes or prefixes: they have the "normal" name space. Classes are given a suffix of "Class" if they are meant to be instanciated or are given a suffix of "AbsClass" if they are an abstract class that provides inheritable implementation but is not complete and instanciable by itself.
All variables, return values, and parameters are typed to an interface (a Type).
Point aPoint = GeometryPack.newPointX_y(3,5);
public Point newPointX_y(float x, float y) {
if ((x == 0) && (y ==0)) return ZERO_POINT;
if (y == 0) return newPointR_theta(x,0); //Just to show the encapsulation
return new PointXYClass(x,y);
}
public Point newPointR_theta(float r, float theta) {
if (r == 0) return ZERO_POINT;
return new PointRThetaClass(r,theta);
}
public Rectangle newRectangle(Point corner1, Point corner2) {
if (corner1.equals(corner2)) return new DegenRectangleClass(corner1);
return new RectangleClass(corner1,corner2);
}
The only place where the Class itself is exposed is within the implementation of the factory method and (obviously) the methods of the actual object.
Classes and abstract classes frequently implement the "unsuffixed" interface directly:
public class PointAbsClass implements Point {...
or indirectly when a parent class implements it for them:
public class PointXYClass extends PointAbsClass {...
Because Java allows multiple inheritance among interfaces and between interfaces and classes, there is much more flexibility of implementation (proxies, performance variations, system refactoring) than if variable typing were to actual classes.
Make sure the name of an interface or a class matches the range of usage of the interface or class. If it is designed to be very general, choose a very simple name. If it is designed to be used in a more specific context, qualify it enough to describe the range of functionality that it is meant to have.
You should spend a fair amount of time making sure your interface and class names are very good. Try names out and fix them if they dont work well in actual use or if they do not fit well with other interfaces and classes in the system. This is especially valuable in the younger stages of a project before other people have mentally and programmatically committed to a name.
I separate specifying an objects public interfaces in two ways: specifying what an object is (its Types) and specifying what an object responds to (its Protocols).
These are both specified using Java interfaces, but protocols tend to be "mixed in" to the Types as opposed to having their own hierarchy. Classes inherit primarily from types.
Java has 4 levels of access control [ See [Gosling+JS 96] for more details on these access controls, especially the formal definition of the protected access level. Java has a more complicated concept of access than Smalltalk would because access is considered from the class perspective instead of from the object perspective. In Java a method being executed within one object can access another object’s private and protected areas if the other object is of the same class. In Smalltalk this would not be allowed: an object would be able to access another object’s methods only if they have public or package visibility. Java has trades the advantage of easy cloning for breaking encapsulation at the object level.] :
|
Public |
Visible to everyone |
|
Protected |
Visible only to the class and its subclasses (when each is acting as a subclass) |
|
Private |
Visible only to the class itself (no subclasses) |
|
Package |
Visible only to the classes within the same package |
I primarily use Public and Protected access control. Since I use interfaces to specify the public interface of a class, the only methods that are public and not in some interface are creation methods (constructors and initialization). I use package visibility if I want a method to be visible to other classes in the package to support implementation that is not part of the public behavior. I rarely use Private because I rarely find it necessary to consider a subclass as not having the ability to access its superclasses implementation. My default order of access is:
|
Protected |
All instance variables and methods that are not part of the public interface to the class. |
|
Public |
Methods that are part of the public Type interface or are publicly needed to create the object |
|
Package |
Any non-public methods that other classes in the package need for implementation |
|
Private |
Special use. |
Although the above access control standards work fine on there own, they do not work well with interfaces which requires methods to be public. The following section overrides the current section because of a limitation in Java.Package Interfaces and Subsystem Interfaces
In a continued attempt to document and encapsulate types and classes, I now use interfaces for package access as well as public access. Unfortunately, Java does not allow you to specify that an interface contains methods that are only package accessible. When you create an interface, its methods are automatically public and that means the methods must be public in the class implementing those methods. So if we use an interface to specify the package interface, all those methods must be public on the class.
Fortunately, this is not a problem, since no client of the class is expected to ever see the class itself, but must instead interact with the type interface that is available to the client. So if a class only has access to a public interface, it can only use the methods specified in that interface and can not normally see or use methods (although also "public" on the class). A class that has the package interface (which is private to the package) will be able to use both the public and the "package public" interfaces.
The standard is still that the public interfaces have no suffixes. The naming convention for the additional Package and Subsystem interfaces is as follows:
|
Pi |
Package Interface: Interface for within Package . This specifies the interface/methods available to other classes in the same package. This is usually the most inclusive interface (other than for the class itself). |
|
Si |
Subsystem Interface: Interface for within Subsystem. This specifies the interface/methods available to other classes in the same subsystem but in a different package. This is more inclusive than the public interface but more restrictive than the package interface. |
Using initial capitalization for words implies abbreviations should be initial capitalized as well. This makes it easier to identify the word boundaries but is against normal abbreviation policies: note that abbreviations are inherently all lowercase and are only capitalized when the start of a word boundary. This also means that if a word is abbreviated to a single letter, there can be a series of capital letters that indicate a series of one letter abbreviated words. DomainStorageInformation can be abbreviated to DSInformation and would have three words (D, S, Information). The previous example of PointXYClass is four words (Point, X, Y, Class). Numbers are considered to start their own word consisting of numbers followed by any non-capitalized letters. For example, Procedure2Arg is three words (Procedure, 2, Arg), 49ers is one word, and root4The49ers is 4 words (root, 4, the, 49ers).
For Java I always use Factories and Factory methods for "public" object construction. I use factory creation methods instead of straight constructors because they
Generally I try to use another appropriate object as the Factory for a particular class (a database object create Tables). For classes that have no other appropriate factory object I use the Pack object as the factory.
It is very useful to be able to treat a Package as an object, so in every package I have a class named "<packageName>Pack" (e.g. java.util would have a "UtilPack" class). This provides clients with a single interface for creating objects, finding singleton objects, and any other "static" (non-object) behavior. It also prevents the problem of exposing a class from behind the interfaces: class constructors and static methods can be private to the package with the pack object exposing what ever functionality is needed. Clients of a package see only the public interfaces and the Packs functionality and have no visibility to the implementation classes.
You can interact with a Pack in two ways: as either a singleton object or as a "static-side" virtual object. The singleton object is more flexible because you can pass the object around within a program (for example, to recurse a package hierarchy). The static object is more convenient for construction (e.g. "UtilPack.newDate()") but is less flexible and more coupled. Overall, convenience tends to win out because construction is the main use of a Pack (but remember that it is better to give Factory responsibilities to another suitable object than the Pack).
![]() |
Previous TOC Next | |
Copyright (c) 1997, Mark L. Fussell. |
mirror of page
http://www.chimu.com/publications/javaStandards/part0005.html