2010-04-30 2 views
7

Что делать, если у меня есть несколько методов фабрики, возвращающих непубличный тип и набор парных методов, которые дают переменные этого непубличного типа? Это приводит к упоминанию предупреждающего сообщения в NetBeans.Экспорт непубличного типа через общедоступный API

В результате открытый API API будет содержать только два набора парных методов. Причина заключается в том, чтобы сделать мою иерархию типов закрытой (например, классы seald в Scala) и разрешить пользователям создавать эти типы только с помощью заводских методов. Поэтому мы получаем DSL в некотором смысле.

Например, класс расписания представлен ограничениями полей календаря. Существуют некоторые типы противопоказаний - Range, Singleton, List, FullSet - с интерфейсом NumberSet в качестве корня. Мы не хотим раскрывать эти типы и как расписываться с ними. Нам просто нужна спецификация от пользователя. Поэтому мы делаем пакет NumberSet-private. В классе Перечне мы создаем несколько фабричных-методы ограничения:

NumberSet singleton(int value); 
NumberSet range(int form, int to); 
NumberSet list(NumberSet ... components); 

и некоторые методы для создания расписания объекта:

Schedule everyHour(NumberSet minutes); 
Schedule everyDay(NumberSet minutes, NumberSet hours); 

Пользователь может использовать их только в порядке:

Schedule s = Schedule.everyDay(singleton(0), list(range(10-15), singleton(8))); 

Это плохая идея?

ответ

9

Идея сама по себе звучит. Но это не сработает, если вы сделаете корневой тип (здесь: 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. Главное преимущество этого: клиент может объявлять переменные этого типа, а компилятор может ввести проверку. Однако ваша инфраструктура остается единственным местом, где могут быть созданы конкретные экземпляры этого типа, и, таким образом, ваша структура имеет полный контроль над всеми значениями.

+0

Почему это не будет работать, если мы сделаем пакет типа root-private? –

+0

Да, но это нежелательный клиент, чтобы увидеть этот тип. Нам просто нужна спецификация через заводские методы и передача ее через заводские методы для объектов Schedule. –

0

я могу думать о двух способов сделать это, но ни один действительно не работает:

  1. Объявить NumberSet как пакет частного, но изменить методы публичных API, которые в настоящее время используют NumberSet типа использовать Object вместо этого, и имеют реализации, приводящие аргументы от Object до NumberSet по мере необходимости. Это зависит от того, что вызывающие абоненты всегда передают правильный тип объекта и будут подвержены ClassCastException.

  2. Внедрение общедоступного интерфейса NumberSet без каких-либо методов и пакета private InternalNumberSet, который реализует NumberSet и определяет методы, необходимые для внутреннего использования. Тип литья используется еще раз, чтобы привести аргументы NumberSet в InternalNumberSet. Это лучший предыдущий подход, но если кто-то создает класс, который реализует NumberSet, не реализуя также InternalNumberSet, результат будет ClassCastException s еще раз.

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

Смежные вопросы