我正在浏览一篇关于良好API设计的博客link text
在一个标题为“重新发明套接字”的示例部分中,它展示了如何对使用它的客户端代码强制执行某些规则和先决条件的设计。例如。客户端必须先调用bind()才能调用connect(),并且必须先连接它才能允许send()或receive()数据。
我对C / C ++比较熟悉,所以我在完全理解类设计如何强制执行API规则方面遇到了一些麻烦。例如,如何防止客户端代码使用以下内容调用此API:
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 */);
编译器如何以及为什么会捕获该错误?如果我正确理解子类继承,SocketConnected是一个SocketBound,它是一个Socket。但是,如果客户端代码能够声明SocketConnected对象,那么如何强制执行必须在允许send()和receive()之前调用bind()和connect()的规则?
由于
答案 0 :(得分:3)
您只需提供bind()
作为创建者,而不是SocketConnected
上的公共构造函数来强制执行规则。除了通过API之外,没有其他方法可以实例化SocketConnected
。是的,您可以声明该类的对象,但您不能自己创建一个;因此,在调用正确的创建者之前,您无法调用任何实例方法。
ADDED:重新评论一下bind().connect()
:这只是一个链接的例子,有点像fluent interface,但是类型限制控制着电话的顺序。想想会发生什么。第一个bind()
调用会创建一个实例,然后您可以在其上调用connect()
。喜欢的作者提供的最终代码示例是对比:这是使用传统 Berkeley样式套接字库的情况,其中s
是套接字在没有编译器抱怨的情况下,bind()
和connect()
都可以以任何顺序调用。
ADDED:重新设计模式 - 我不认为这已被命名。它可能应该是。它支持fail fast的设计标准的变体,尽早在编译阶段失败。
答案 1 :(得分:2)
关键是他正在创建仅定义为返回绑定套接字的接口。您将获得一个仅定义为返回绑定/连接套接字的提供程序。
如果你有他的SocketBound实例
public interface SocketBound {
SocketConnected connect(Address<?> address, int port);
}
您只能从中获取SocketConnected。