views:

74

answers:

2

I was looking at a blog about good API design link text

In one of the example sections titled 'Reinventing the Socket', it showed a design of how to enforce certain rules and prerequisite on the client code that uses it. eg. the client must call bind() before it can call connect(), and it must be connected before it's allowed to send() or receive() data.

I'm more familiar with C/C++ so I'm having some trouble fully comprehending how the class design is enforcing the API rules. Like for example, how do you prevent client code from making calls into this API with something like this:

SocketConnected s = socket.bind(localaddress, 1000);

//client doesn't call the connect() method
//and just calls the send() method right away.

//this line should give compile-time error
//because only bind() was called but not connect()
s.send(/* some data goes here */);

How and why would the compiler catch that error? If I'm understanding the subclass inheritance correctly, SocketConnected is-a SocketBound which is-a Socket. But if the client code is able to declare a SocketConnected object, how can you enforce the rule that bind() and connect() must be called before send() and receive() are allowed?

Thanks

+3  A: 

You enforce the rules by only providing bind() as creator, and no public constructor on SocketConnected. There's no other way of instantiating a SocketConnected except through the API. Yes, you can declare an object of that class, but you cannot create one by yourself; hence you cannot call any instance methods until the proper creator has been called.

ADDED: Re your comment on bind().connect(): that's just an example of chaining, somewhat like a fluent interface, but with type restrictions controlling the order of calls. Think about what happens. The first bind() call creates an instance, on which you then can call connect(). The final code example the likned author provides is a contrast: that's what things would look like with a traditional Berkeley style socket library, where the s is a socket on which both bind() and connect() are possible to call, in any order, without the compiler complaining.

ADDED: Re design pattern - I don't think this has been named. It probably should be. It supports a variation of the design criterion of fail fast, by failing as early as at the compiler stage.

Pontus Gagge
Does this correspond to any design pattern I can look into more detail?
Victor T.
+2  A: 

The point is that he's creating interfaces that are defined only to return bound sockets. You get a provider that is only defined to return bound/connected sockets.

if you have an instance of his SocketBound

 public interface SocketBound {
        SocketConnected connect(Address<?> address, int port);
    }

You can only get a SocketConnected from it.

Steve B.
near the bottom, the author also wrote: s.bind(localAddr, 1000); s.connect(remoteAddr, 70);How does that make sense? Is s a SocketConnected or is it a SocketBound or what object is it?
Victor T.