I was looking at a blog about good API design link text
In one of the example sections titled 'Reinve开发者_JAVA百科nting 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
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.
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.
精彩评论