2009-04-28

Object states, roles, and access modifiers

Johan sometimes says that he thinks objects should have their life cycles explicit. When you first create an object, only some calls are valid (e.g. for a socket object, only calls for setting up how it should connect and the connect method). Later on, more functionality is available (in our case, methods for reading, writing, and closing the socket). This should be explicit. Something like this:

public interface SocketFactory
{
UnconnectedSocket createSocket()
}
public interface UnconnectedSocket
{
void bind(IPEndPoint localAddress);
ConnectedSocket connect(IPEndPoint remote);
}
public interface ConnectedSocket
{
int read(byte[] buf, int offset, int count);
int write(byte[] buf, int offset, int count);
ClosedSocket close();
}
public interface ClosedSocket
{
}
class Socket : UnconnectedSocket, ConnectedSocket, ClosedSocket
{
// ...
}

This is similar to a way of writing three-layer web applications that I've used in the past: the presentation layer requests an obejct from the logic layer, in which it writes the parameters for the query. It then passes it to the logic layer, which can augment the query, cache, etc. If necessary, the query is sent to the data layer along with a response object. The data layer uses the request object to look up some data, and writes it into the response object and returns. The logic layer then augments, caches (etc.) the response, and gives it to the presentation layer. The easy way of doing it it to just have a single class for creating the query (presentation), for reading the query (data), for writing the response (data), and for reading the response (presentation). In some cases, you might need to use different classes. A solution is to have the class in the presentation layer, and let it implement four interfaces for the different ways of using it. Something like:

interface WritableUserRequest
{
void setUsername(String username);
}
interface ReadableUserRequest
{
String getUsername();
}
interface WritableUserResponse
{
void setRealName(String realName);
}
interface ReadableUserResponse
{
String getRealName();
}
class User implements WritableUserRequest, ReadableUserRequest, WritableUserResponse, ReadableUserResponse
{
private String userName;
private String realName;
public void SetUserName(String userName)
{
this.userName=userName
}
// Similar for all getters and setters
}

In essence, this is about having one object let different objects interact with it in different ways at different times. We can group the methods in different little packets and hand some packets to some clients, and others to others. Some methods will be in many packets, some in only one.

Which brings us to the final part of the title: access modifiers. Access modifiers lets us do something like this, but in a very primitive way. We get four different groups of methods, one for everyone, one for only the same class, one for all the subclasses, and the funny, nameless exact-same-package-only group. A more flexible mechanism for grouping methods would save us from all the needless typing that creating interfaces for each method group. Come to think of it, there is another way: annotations. A follow-up with some annotation processing goodness forthcoming, some time after I've written about error handling and published my other little hacks which are the reason Johan got me into writing here in the first place.