在我的项目jOOQ中,我使用复杂的数据结构为SQL查询建模。查询的所有组件都实现
public interface QueryPart {
int bind(java.sql.PreparedStatement stmt);
int bind(java.sql.PreparedStatement stmt, int initialIndex);
SQLDialect getDialect();
String toSQLDeclaration();
String toSQLDeclaration(boolean inlineParameters);
String toSQLReference();
String toSQLReference(boolean inlineParameters);
}
此接口的方法由库的所有包内部使用,以构造和执行SQL。不应直接从客户端代码调用它们。为此,我添加了
public interface QueryPartProvider {
QueryPart getQueryPart();
}
这是唯一公开的界面。实际查询部分的示例是:
public interface Table extends QueryPartProvider {}
class TableImpl implements QueryPart, Table {}
如您所见,QueryPart方法只能通过Table.getQueryPart().toSQLDeclaration()
等进行访问。
我的设计有助于阻止对QueryPart方法的直接访问,但无法完全隐藏它。我的问题是:有人能告诉我一个好的设计模式来实现这个目标吗?
注意:最简单但不太好的解决方案是将所有对象强制转换为QueryPart,例如: ((QueryPart) table).toSQLDeclaration()
答案 0 :(得分:5)
界面的所有方法都是公共的,因此您无法访问库客户端无法访问的内容。
也许你可以使用Table
的抽象类和作为包受保护的getQueryPart()
方法来实现你想要的。我不确定我是否愿意这样做,而不是从Table
转换为TableImpl
。
答案 1 :(得分:2)
在实现类似于 sfussenegger 建议的内容之后,我想出了一个更好的解决方案,涉及适配器设计模式。这是大纲:
/**
* Objects providing an internal API implement this interface
*/
public interface Adapter {
/**
* Dynamically expose an (publicly unknown) internal API.
*/
<T> T internalAPI(Class<T> internalType) throws ClassCastException;
}
此适配器类型是向公众公开内部任何内容的唯一事实。只有包私有实现方法知道这个方法的可能参数(以及那些真正想要实际使用内部API进行解决方法,扩展等的黑客)。
/**
* This type contains the public API for a QueryPart
*/
public interface QueryPart extends Adapter {
// [...]
}
/**
* This type contains the internal API for a QueryPart
*/
public interface QueryPartInternal extends QueryPart {
// [...]
}
上面的QueryPart和QueryPartInternal是相关的。这个事实是公开的,但没有公共类/类型扩展QueryPartInternal。只有以下的package-private类及其数量众多的子类:
/**
* This class is the base class for all QueryParts.
* It is package private and thus doesn't expose anything
*/
abstract class AbstractQueryPart implements QueryPartInternal {
// [...]
/**
* For other package private implementation methods
*/
@Override
public final <T> internalAPI(Class<T> internalType) {
return internalType.cast(this);
}
/**
* Convenience method for subclasses heavily using the
* internal API
*/
protected final QueryPartInternal internal(QueryPart part) {
return part.internalAPI(QueryPartInternal.class);
}
// [...]
}
答案 2 :(得分:1)
你能解释一下你为什么要那样做吗?我能看到的唯一原因是无法为库的用户实现接口。
我认为这不是一个好方法。只需添加一些Javadoc并解释为什么实现它没有意义。但最后,将它留给用户是否有合理的理由来创建自定义实现。总是很难预见每一个用例。
如果有人坚持他的方法,那肯定不是你的错 - 他不能说他没有被警告过:)
举个例子,这就是你可以在Apache Wicket的源代码中找到的东西:
/**
* THIS IS WICKET INTERNAL ONLY. DO NOT USE IT.
*
* Traverses all behaviors and calls ...
*/
编辑: 只是另一个:你可以尝试这个,虽然我仍然不鼓励它 - 不要说你没有被警告过;)
public interface ExposedInterface {
void foo();
}
// only default visibility
interface InternalInterface extends ExposedInterface {
// nothing here
}
// and here some methods
ExposedInterface get(); // user can use it
void set(InternalInterface obj); // user is out of luck here