Ответ 1
Вы собираетесь внедрить в Java язык, специфичный для домена (DSL). Некоторые будут ссылаться на ваш DSL как на "внутренний" DSL, потому что вы хотите использовать для него стандартные Java-конструкции, а не "внешние" DSL файлы, которые намного мощнее (SQL, XML, любой тип протокола), но имеют который будет построен примитивно с использованием конкатенации строк.
Наша компания поддерживает jOOQ, которая моделирует SQL как "внутренний" DSL в Java (об этом также упоминалось в одном из комментариев), Моя рекомендация для вас заключается в следующем:
- Узнайте, как должен выглядеть ваш язык. Не думайте в терминах Java ( "внутренний" DSL) сразу. Подумайте о своем собственном языке ( "внешний" DSL). Тот факт, что вы реализуете его на Java, не должен быть важен в этот момент. Возможно, вы даже реализуете его в XML, или вы напишете для него свой собственный синтаксический анализатор/компилятор. Сначала подумайте о своей спецификации языка, прежде чем внедрять ее в Java, сделает ваш DSL более выразительным, более интуитивно понятным и более расширяемым.
- Как только вы решите использовать общий синтаксис и семантику своего языка, попробуйте использовать нотацию BNF на вашем языке. Вначале вам не обязательно быть слишком точным, но это придаст ему некоторые формальные аспекты. Железнодорожные диаграммы - очень хороший инструмент для этого. Вы узнаете, какие комбинации возможны, а какие нет. Кроме того, это хороший способ создать общую документацию по языку, поскольку однопользовательский Javadocs не будет очень полезен для ваших новичков.
- Когда у вас есть формальный синтаксис, следуйте правилам, которые мы упоминали в нашем блоге: http://blog.jooq.org/2012/01/05/the-java-fluent-api-designer-crash-course. Эти правила оказались очень полезными при разработке jOOQ API, о котором наши пользователи сообщили очень интуитивно (если они уже знают SQL, то есть).
Моя личная рекомендация для вас такова:
-
is
,has
,capableOf
и т.д. являются предикатами factory. Статические методы - ваш лучший выбор на Java, потому что вы, вероятно, захотите передать предикаты другим методам DSL вашего API. Я не вижу никаких проблем с интеграцией IDE, автозаполнением или документацией, если вы поместите их в один класс factory. В частности, Eclipse имеет приятные функции для этого. Вы можете поместитьcom.example.Factory.*
в свои "избранные", что приводит к тому, что все методы доступны повсюду из раскрывающегося списка автоматического завершения (что снова является хорошей точкой доступа для Javadocs). В качестве альтернативы, ваш пользователь может просто статически импортировать все методы из factory там, где он им нужен, что имеет тот же результат. -
and
,or
,not
должны быть методы для типа предиката (not
также может быть центральным статическим методом). Это приводит к нотации infix для булевых комбинаций, что многие разработчики считают более интуитивным, чем то, что сделала JPA/CriteriaQuery:public interface Predicate { // Infix notation (usually a lot more readable than the prefix-notation) Predicate and(Predicate... predicate); Predicate or(Predicate... predicate); // Postfix notation Predicate not(); // Optionally, for convenience, add these methods: Predicate andNot(Predicate... predicate); Predicate orNot(Predicate... predicate); } public class Factory { // Prefix notation public static Predicate not(Predicate predicate); }
-
Для объединений у вас есть несколько вариантов. Некоторые примеры (которые вы также можете комбинировать):
// Prefix notation public class Factory { public static Query union(Query... queries); } // Infix notation public interface Query { Query union(Query... queries); }
-
И последнее, но не менее важное: если вы хотите избежать ключевого слова
new
, которое является частью языка Java, а не вашего DSL, также создайте запросы (точки входа вашего DSL) из Factory:// Note here! This is your DSL entry point. Choose wisely whether you want // this to be a static or instance method. // - static: less verbose in client code // - instance: can inherit factory state, which is useful for configuration public class Factory { // Varargs implicitly means connecting predicates using Predicate.and() public static Query query(Predicate... predicates); }
В этих примерах вы можете построить запросы как таковые (ваш пример):
высокий баскетболист по имени [майкл ИЛИ Деннис]
СОЕДИНЕНИЕ
серебряная ложка, изогнутая и блестящая
Версия Java:
import static com.example.Factory.*;
union(
query(is("tall"),
capableOf("basketball"),
name("michael").or(name("dennis"))
),
query(color("silver"),
a("spoon"),
is("bent"),
is("shiny")
)
);
Для дальнейшего вдохновения посмотрите jOOQ, а также на jRTF, что также отлично работает при моделировании RTF ( "внешний" DSL) в Java как "внутренний" DSL