Идея сама по себе является звуком. Но это не сработает, если вы сделаете пакет root type (здесь: NumberSet
) закрытым. Либо предоставьте этот тип, либо используйте открытый интерфейс. Фактические типы реализации должны (и могут) быть скрытыми.
public abstract class NumberSet {
// Constructor is package private, so no new classes can be derived from
// this guy outside of its package.
NumberSet() {
}
}
public class Factories {
public NumberSet range(int start, int length) {
return new RangeNumberSet(start, length);
}
// ...
}
class RangeNumberSet extends NumberSet {
// ... must be defined in the same package as NumberSet
// Is "invisible" to client code
}
Редактировать Предоставление скрытого / закрытого корневого типа из общедоступного API является ошибкой. Рассмотрим следующий сценарий:
package example;
class Bar {
public void doSomething() {
// ...
}
}
public class Foo {
public Bar newBar() {
return new Bar();
}
}
и рассмотрим клиентское приложение, использующее этот API. Что может сделать клиент? Он не может должным образом объявить переменную типа Bar
, поскольку этот тип невидим для любого класса вне пакета example
. Он даже не может вызывать public
методы для экземпляра Bar
, который он получил откуда-то, поскольку он не знает, что такой открытый метод существует (он не может видеть класс, не говоря уже о любых членах, которые он предоставляет). Итак, лучшее, что может сделать клиент, это что-то вроде:
Object bar = foo.newBar();
что по сути бесполезно. Другое дело было бы иметь открытый интерфейс (или абстрактный класс) вместо закрытого пакета, как в коде, определенном выше. В этом случае клиент на самом деле может объявить переменную типа NumberSet
. Он не может создавать свои собственные экземпляры или наследовать подклассы, поскольку конструктор скрыт от него, но он может получить доступ к общедоступному API, определенному.
Редактировать снова Даже если вам нужно «безликое» значение (с точки зрения клиента), т. Е. Значение, которое не определяет какие-либо интересные API, которые клиент может захотеть вызвать, оно все равно хорошая идея выставить публичный базовый тип описанным выше способом. И только для того, чтобы компилятор мог выполнять проверку типов и позволять клиентскому коду временно сохранять такое значение в (должным образом объявленной) переменной.
Если вы не хотите, чтобы ваш клиент вызывал какие-либо методы API для типа: это нормально. Ничто не мешает вам не предоставлять публичный API для вашего (иначе) публичного базового типа. Просто не объявляй ничего. Используйте «пустой» абстрактный базовый класс (пустой с точки зрения клиента, так как все интересные методы будут частными и, следовательно, скрытыми). Но, тем не менее, вы должны предоставить публичный базовый тип, или вы должны использовать обычный Object
в качестве возвращаемого значения, но тогда вы потеряете проверку ошибок во время компиляции.
Практическое правило. Если клиент должен вызвать какой-то метод для получения значения и передать его другому API, то клиент на самом деле знает , что существуют некоторые магические специальные значения. И он должен быть в состоянии справиться с этим каким-либо образом (по крайней мере, «передать его»). Если вы не предоставите клиенту правильный тип для работы со значениями, вы ничего не купите, кроме (совершенно уместных) предупреждений компилятора.
public abstract class Specification {
Specification() {
// Package private, thus not accessible to the client
// No subclassing possible
}
Stuff getInternalValue1() {
// Package private, thus not accessible to the client
// Client cannot call this
}
}
Приведенный выше класс является «пустым» в отношении кода клиента; он не предлагает пригодный для использования API, за исключением того, что Object
уже предлагает. Основное преимущество - клиент может объявлять переменные этого типа, а компилятор может проверять тип. Ваша платформа, тем не менее, остается единственным местом, где могут быть созданы конкретные экземпляры этого типа, и, таким образом, ваша среда имеет полный контроль над всеми значениями.