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

发布于 2024-10-05 09:44:08 字数 1048 浏览 1 评论 0原文

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

Which is the only publicly expose 接口。实际查询部分的一个示例是:

public interface Table extends QueryPartProvider {}
class TableImpl implements QueryPart, Table {}

如您所见,QueryPart 方法只能通过 Table.getQueryPart().toSQLDeclaration() 等访问。

我的设计有助于阻止直接访问 QueryPart 方法,但无法完全隐藏。我的问题是:谁能告诉我一个好的设计模式来实现这个目标?

注意:最简单但不是很好的解决方案是将所有对象转换为 QueryPart,例如 ((QueryPart)表).toSQLDeclaration()

In my project jOOQ, I model SQL queries with a complex data structure. All components of a query implement

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);
}

This interface's methods are used internally by all packages of the library to construct and execute SQL. They should not be invoked directly from client code. For that purpose, I have added

public interface QueryPartProvider {
  QueryPart getQueryPart();
}

Which is the only publicly exposed interface. An example of an actual query part is:

public interface Table extends QueryPartProvider {}
class TableImpl implements QueryPart, Table {}

As you can see, the QueryPart methods can only be accessed via Table.getQueryPart().toSQLDeclaration(), etc.

My design helps discouraging direct access to QueryPart methods, but cannot completely hide it. My question is: Can anyone tell me a good design pattern to achieve this goal?

Note: The simplest but not very nice solution would be to cast all objects to QueryPart, e.g. ((QueryPart) table).toSQLDeclaration()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

琉璃梦幻 2024-10-12 09:44:08

接口的所有方法始终是公共的,因此您无法访问库客户端也无法访问的内容。
也许您可以使用 Table 的抽象类和受包保护的 getQueryPart() 方法来实现您想要的目的。但我不确定我是否想要这样做,而不是从 TableTableImpl 的转换。

All methods of an interface are always public, so there is no way for you to have access to something which is not accessible to your library clients as well.
Maybe you could achieve what you want using an abstract class for Table, and the getQueryPart() method as package protected. I'm not sure however that I would want to do that, instead of a cast from Table to TableImpl.

暗喜 2024-10-12 09:44:08

在实现了与 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。只有以下包私有类及其无数子类可以:

/**
 * 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);
  }
  // [...]
}

After implementing something similar to what sfussenegger suggested, I came up with an even better solution involving the Adapter design pattern. This is the general outline:

/**
 * 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;
}

This adapter type is the only fact exposed to the public about anything internal. Only package private implementation methods know about the possible arguments to this method (and those hackers that really want to actually use the internal API for workarounds, extensions, etc).

/**
 * 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 {
// [...]
}

The above QueryPart and QueryPartInternal are related. This fact is known to public but no public class / type extends QueryPartInternal. Only the following package-private class and its gazillion subclasses do:

/**
 * 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);
  }
  // [...]
}
灯角 2024-10-12 09:44:08

您能解释一下为什么您想这样做吗?我能看到的唯一原因是使您的库的用户无法实现该界面。

我认为这不是一个好方法。只需添加一些 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

Could you please explain why you'd like to do that? The only reason I can see is to make it impossible to implement the interface for a user of your library.

I don't think that's a good approach. Simply add some Javadoc and explain why it doesn't make sense to implement it. But finally, leave it to the user whether there's a valid reason to create a custom implementation. It's always difficult to foresee each and every use case.

If somebody gots stuck with his approach it's certainly not your fault - he can't say he hasn't been warned :)

To give an example, that's what you can find all over Apache Wicket's source code:

/**
 * THIS IS WICKET INTERNAL ONLY. DO NOT USE IT.
 * 
 * Traverses all behaviors and calls ...
 */

EDIT:
just another though: you could try this, although I'd still discourage it - don't say you haven't been warned ;)

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文