2015-09-23 2 views
5

Я прочитал и получил удовольствие от статьи http://blog.jooq.org/2012/01/05/the-java-fluent-api-designer-crash-course/ от Lukas Eder, и я хотел бы создать Fluent Interface для класса.Свободный интерфейс (Java) для объекта с геттерами и сеттерами

Класс имеет четыре функции («слова» fill1 to fill4), которые позволяют устанавливать атрибуты объектов и четыре функции («слова» get1 to get4), которые получают эти атрибуты, но только если установлены необходимые атрибуты:

Сначала я должен заполнить основные настройки (fill1). После этого я либо могу получить некоторые из этих настроек (get1 to get3), которые являются строками. Или я могу заполнить дополнительную информацию (fill2 to fill4). Но только после каждый fill2 to fill4 был вызван хотя бы один раз, может быть вызван окончательный get4. Как мне это сделать?

Первый граф (состояния - черные точки) показывает, что я хочу делать, но как вы можете видеть? отмечает часть, которая не ясна, потому что, если она оставлена, как она есть в первом графе, она позволит вызывать get4, даже если был вызван только один из fill2 to fill4.

Второй график заставит каждого fill2 заполнить4 вызывается, но информирует порядок и ограничивает это, если я хочу изменить, например. fill3, мне также нужно сбросить fill2 и fill4.

Последний график будет делать то, что я хочу, но он имеет состояний! Теперь, если я предполагаю, что просто добавлю еще один атрибут в группу fill2, чтобы заполнить4, число состояний взорвется еще больше.

enter image description here

Edit: Кроме того, после того, как думать об этом еще немного, я заметил, что, как я бы это сделать (смотри ниже), даже не будет в состоянии осуществить последний график, потому что после того, как fill2 вызывается, мы можем быть в разных состояниях - в зависимости от того, что произошло раньше.

Что делать/делать?

Редактировать: Я реализую свои беглые интерфейсы немного как фасад (если у меня есть шаблон дизайна справа). Я имею в виду: я оставляю фактический класс почти нетронутым - возвращая его (как в цепочке методов), но имея соответствующие интерфейсы состояния как возвращаемые значения в сигнатурах метода. Состояния представлены вложенными интерфейсами. Пример:

public class MyClass implements MyInterface.Empty, MyInterface.Full { 

    String stuff; 

    private MyClass(){}; 

    public static MyInterface.Empty manufactureNewInstance(){ 
     return new MyClass(); 
    } 

    public MyInterface.Full fillStuff(String stuff){ 
     this.stuff = stuff; 
     return this; 
    } 

    public String getStuff(){ 
     return stuff; 
    } 

} 

public interface MyInterface { 

    public interface Empty { 
     public MyInterface.Full fillStuff(); 
    } 

    public interface Full { 
     public String getStuff(); 
    } 

} 

public class MyMain { 

    pulic static void main(String[] args) { 

     // For explaination: 
     MyClass.Empty myclassEmpty = MyClass.manufactureNewInstance(); 
     MyClass.Full myclassFull = myclassEmpty.fillStuff("Hello World 1!"); 
     String result1 = myclassEmpty.getStuff(); 

     // As fluent chaining: 
     String result2 = MyClass.manufactureNewInstance() 
      .fillStuff("Hello World 2!") 
      .getStuff(); 

    } 

} 

ответ

5

Fluent API для интерфейса в Java несколько ограничены тем, что вам нужно для представления каждого состоянияAPI другого типа. Это является следствием того, что типы Java являются единственным способом связывания набора ограничений API с Java-компилятором для проверки.

Очевидно, что это несчастливое ограничение при создании свободного API. Чтобы преодолеть это ограничение, вам необходимо либо:

  1. Реализовать все API состояния вручную, например, интерфейс. Это может быть приемлемым решением, если ваш код вряд ли изменится, например. в проекте с довольно ограниченным объемом, который не должен слишком долго жить. Один класс поддержки может затем реализовать все интерфейсы, которые представляют каждое состояние API .Пока пользователи не используют выражения отражения или типа, компилятор проверяет, что методы вызываются в правовом порядке.

  2. Автоматическое создание кода. Это более амбициозный подход, но он может сэкономить вам много усилий по набору текста и рефакторинга, если ваше состояние API -соединения «взрываются», как вы говорите. Я написал библиотеку генерации кода, которая называется Byte Buddy, и я знаю пользователей, использующих библиотеку для создания интерфейсов для свободного API. (К сожалению, два пользователя, с которыми я общался по этому вопросу, не открывали исходный код.) Если вы предпочитаете создавать исходный код Java вместо байт-кода Java, тогда Java poet может быть альтернативой для вас, который у меня также есть видно для этого кейс.

  3. Упростите свой API только для проверки наиболее распространенных ошибок при проверке на менее распространенные ошибки за исключениями во время выполнения. Это часто является приемлемым решением, поскольку он делает API более доступным.

+0

Большое спасибо. Эти два проекта генерации кода кажутся увлекательными. Написание всего этого кода для генерации кода, похоже, не быстрее, чем просто писать интерфейсы/классы напрямую. Я обновил свой вопрос относительно части фасада. – Make42

+0

Генерация кода может помочь вам автоматически разрешить все перестановки вашего свободного интерфейса. Например, я мог бы попросить вас написать программу, которая печатает все перестановки чисел 1-5. Это будет короткая программа, но для записи всех чисел потребуется много времени. Это похоже на ваш свободный интерфейс. Вы можете описать генератор кода, как создать все возможные перестановки без необходимости записывать их самостоятельно. Это часто меньше подвержено ошибкам (особенно при рефакторинге) и быстрее реализуется. –

+0

Хорошая точка. Это может помочь найти ответ на мой вопрос, но пока не отвечает (см. Правки). Тем не менее: Большое спасибо. – Make42