Как не выставлять публичный интерфейс на Java - PullRequest
2 голосов
/ 03 декабря 2010

В моем проекте 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 ]

5 голосов
/ 03 декабря 2010

Все методы интерфейса всегда общедоступны, поэтому у вас нет возможности получить доступ к чему-то, что также не доступно вашим клиентам библиотеки.Возможно, вы могли бы добиться того, чего хотите, используя абстрактный класс для Table и метод getQueryPart() в качестве защищенного пакета.Однако я не уверен, что хотел бы сделать это вместо приведения от Table до TableImpl.

2 голосов
/ 09 апреля 2011

После реализации чего-то похожего на то, что предложил 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.Только следующий приватный класс пакета и его подклассы gazillion делают:

/**
 * 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);
  }
  // [...]
}
1 голос
/ 03 декабря 2010

Не могли бы вы объяснить, почему вы хотите это сделать?Единственная причина, которую я вижу, состоит в том, чтобы сделать невозможным реализацию интерфейса для пользователя вашей библиотеки.

Не думаю, что это хороший подход.Просто добавьте 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...