Статьи

Слабосвязанные уровни данных для приложений CRUD

Давайте добавим еще один уровень развязки в наше приложение. Прямо сейчас (после части 1 , части 2 , части 3 и части 4 ), хотя наш пользовательский интерфейс больше не тесно связан с нашей базой данных, ссылка на него все еще существует. И это потому, что во всем пользовательском интерфейсе мы ссылаемся на объект «TripQuery», который использует объект «TripQueryDAO» для подключения к базе данных MySQL через JPA (Eclipselink, с файлом «persistence.xml») , Теперь предположим, что мы хотим обеспечить возможность того, что пользователь не имеет доступа к базе данных MySQL, но, например, к базе данных Oracle. Или, может быть, пользователь вообще не хочет работать с базой данных. Вместо этого пользователь хочет сохранить поездки в текстовый файл.

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

Для всех этих различных сценариев слабосвязанный слой данных очень удобен. Мы собираемся начать с создания нового интерфейса под названием «ITripQuery» в нашем модуле API:

Вот определение «ITripQuery»:

import java.util.List;
import org.my.libs.Trip;
import org.openide.util.Lookup;

public interface ITripQuery {

Lookup getLookup();

List getTrips();

}

Этот интерфейс приятный и простой. Каждый другой тип TripQuery помещает свои возможности в «getLookup» и возвращает свой текущий список объектов Trip через «getTrips». (Кстати, так же работает API проекта NetBeans. У него есть метод getLookup и метод getProjectDirectory. Не верьте мне? Нажмите здесь! Именно благодаря этому очень общему определению API для проектов поддержка Grails и Griffon в IDE NetBeans позволяет распознавать проекты Grails и Griffon просто по структуре проекта, без необходимости создания какой-либо искусственной папки или файла внутри этих проектов, т. е. в соглашениях о переконфигурировании используется слабость API-интерфейса Project, который, в свою очередь, активируется методом «getLookup», который позволяет динамически добавлять случайный набор возможностей.)

Теперь мы можем создать несколько разных модулей, которые реализуют этот сервис. Ниже вы видите службу с именем «TripDummy» и другую службу с именем «TripSQL», например:

«TripSQL» работает с использованием кода базы данных, предоставленного в предыдущих 4 частях этой серии. «TripDummy» вообще не имеет доступа к какой-либо базе данных. Он имеет несколько фиктивных кодов, просто для того, чтобы предоставить некоторые очень упрощенные функциональные возможности, чтобы разработчик мог работать с приложением, не требуя базы данных, как этот, и принять к сведению первый оператор, который регистрирует эту реализацию в «META-INF / services». «, где» Lookup.getDefault «найдет его:

@ServiceProvider(service = ITripQuery.class)
public class DummyTripQuery implements ITripQuery {

private List trips;
private Lookup lookup;
private InstanceContent instanceContent;

@Override
public org.openide.util.Lookup getLookup() {
trips = new ArrayList();
// Create an InstanceContent to hold abilities...
instanceContent = new InstanceContent();
// Create an AbstractLookup to expose InstanceContent contents...
lookup = new AbstractLookup(instanceContent);
instanceContent.add(new ReloadableEntityCapability() {
@Override
public void reload() throws Exception {
Random generator = new Random();
Trip trip = new Trip();
trip.setPersonid(new Person(generator.nextInt()));
trip.setDepcity("Amsterdam");
trip.setDestcity("Prague");
Triptype tt = new Triptype();
tt.setDescription("Description 1");
tt.setTriptypeid(generator.nextInt());
tt.setName("John Smith");
tt.setLastupdated(new Date());
trip.setTriptypeid(tt);
trip.setTripid(generator.nextInt());
getTrips().add(trip);
}
});
instanceContent.add(new CreatableEntityCapability() {
@Override
public void create(Trip trip) throws Exception {
Random generator = new Random();
trip.setPersonid(new Person(generator.nextInt()));
Triptype tt = new Triptype();
tt.setDescription("Desctiption 2");
tt.setTriptypeid(generator.nextInt());
tt.setName("Harry Jones");
tt.setLastupdated(new Date());
trip.setTriptypeid(tt);
trip.setTripid(generator.nextInt());
getTrips().add(trip);
}
});
instanceContent.add(new RemovableEntityCapability() {
@Override
public void remove(Trip trip) throws Exception {
getTrips().remove(trip);
}
});
instanceContent.add(new SaveableEntityCapability() {
@Override
public void save(Trip trip) throws Exception {
StatusDisplayer.getDefault().setStatusText("Saved...");
}
});
return lookup;
}

@Override
public List getTrips() {
return trips;
}

}

Конечно, разработчик может просто жестко закодировать некоторые фиктивные значения в пользовательский интерфейс. Однако это именно та проблема, которую мы пытаемся здесь избежать. Мы хотели бы отделить наш код доступа к данным от пользовательского интерфейса, чтобы мы могли подключать к нему различные источники данных, не требуя каких-либо изменений в нашем пользовательском интерфейсе. Например, в программе просмотра TopComponent у меня есть этот фрагмент, который передает все, что в настоящее время зарегистрировано как реализацию «ITripQuery», для создания иерархии узлов:

ITripQuery query = Lookup.getDefault().lookup(ITripQuery.class);
node = new RootNode(query);
em.setRootContext(node);

Для случая использования, который мы пытаемся охватить здесь, всегда будет зарегистрирована только одна реализация ITripQuery, поэтому нам нужно обслуживать только одну реализацию. Красиво, легко и чисто. Теперь в приложении я могу изменить все ссылки на «TripQuery» на «ITripQuery». А потом, практически не занимаясь работой, у меня в приложении CRUD есть слабосвязанный слой данных.

Спасибо Тони Эппле из Эпплтона за советы и помощь по этому сценарию!

Предыдущие части этой серии, посвященные слабосвязанным функциям для приложений CRUD: