如何不在Java中公开公共接口

时间:2010-12-03 08:31:08

标签: java design-patterns interface

在我的项目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()

3 个答案:

答案 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