Идея сама по себе звучит. Но это не сработает, если вы сделаете корневой тип (здесь: 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.
Edit снова Даже если вы хотите «безликий» значение (с точки зрения клиента), то есть, значение, которое не определяет какие-либо интересные Apis клиент может хотите позвонить, он по-прежнему хорошая идея выставить публичный базовый тип описанным выше способом. И только для того, чтобы компилятор мог выполнять проверку типов и позволял клиентскому коду временно хранить такое значение в (правильно объявленной) переменной.
Если вы не хотите, чтобы ваш клиент вызывал какие-либо методы 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
. Главное преимущество этого: клиент может объявлять переменные этого типа, а компилятор может ввести проверку. Однако ваша инфраструктура остается единственным местом, где могут быть созданы конкретные экземпляры этого типа, и, таким образом, ваша структура имеет полный контроль над всеми значениями.
Почему это не будет работать, если мы сделаем пакет типа root-private? –
Да, но это нежелательный клиент, чтобы увидеть этот тип. Нам просто нужна спецификация через заводские методы и передача ее через заводские методы для объектов Schedule. –