I've just got my SCJD assignment (B&S) and have some initial thoughts on the data access.
In the assignment I have a DB interface and I have to have a Data class that has to act as the data access object.
I have created a small prototype of the data access in the 3 modes (client/server/alone).
Here's what I've done - I'll try and make it clear!
Data implements DB. I have added a private reference in Data to _another_ DB implementing object. This reference is created in the constructor to Data, which calls a Factory object.
The factory determines what type of DB implementation to pass back (either a local or remote one, depedant on a cmd line parameter). The "local" implementation will be the actual DB implementation that goes to the database file. The "remote" implementation is basically a wrapper around a remote reference to a RemoteDB object(which has the same interface as DB, but each method also throws RemoteException) . This implementation will catch all the remote exceptions, but throw all the exceptions that the DB interface methods throw. The Data object, for each method, calls the same method on its private DB reference returned from the factory.
The RemoteDB object that exists on the server is a wrapper around the servers Data object, which will always be a "local" implementation. This wrapper is just a 1:1 method mapper, but because each method in RemoteDB also throws RemoteException, it can be used to access the local Data object on the server, remotely from the client. I would envisage this object being expanded in some kind of management role.
So, the client code only needs to say "DB data = new Data();" and it gets an implementaiton from the factory, which gets the remote implementation from the server. On the server side, it just returns a simple adapter in one line "DBRemoteInterface remoteDBAdapter = new RemoteDBAdapter(new Data());".
I'm wondering if this is a little OTT though? An optimisation would be to create a new interface the same as DB but where the methods all threw exception (DBAllExceptions). I could then replace the private DB reference in Data with a reference to a DBAllExceptions object which would be used to access both the local and remote implementations directly. This would then remove the need for the client side wrapper around the remote implementation, but would mean that the Data class would have to do all the exception handling for every implementation.
The approach I have taken at the moment has a lot of encapsulation going on. The Data class doesnt need to know about any implementation specific details, but I cant see it getting any bigger, because really it doesnt have any implementation in it except for calling methods on actual implementations returned by the factory. However, that does make adding future implementations much easier as the Data class wouldnt have to change at all and new implementations would be very self-contained.
I'm very wary of over-engineering my solution and would be grateful for some feedback.
I hope this isnt too much detail also, but its pretty hard to explain without be verbose.
If I am understanding your explanation correctly, then I think this is reasonable - it might be easier to see if you had a class diagram. I think your rule of thumb should be "A clear design, such as will be readily understood by junior programmers, will be preferred to a complex one, even if the complex one is a little more efficient."