Традиционный способ интеграции объектно-ориентированного бэкэнда с внешней системой — через объекты передачи данных , которые сериализуются в JSON перед выходом и десериализуются при возвращении. Этот способ так же популярен, как и неправильный. Часть сериализации должна быть заменена принтерами , которые я объяснил ранее. Вот мой взгляд на десериализацию, которая должна быть сделана — угадайте, что — объектами.
Скажем, есть внутренняя точка входа, которая должна зарегистрировать новую книгу в библиотеке, поступающую в JSON:
|
1
2
3
4
5
|
{ "title": "Object Thinking", "isbn: "0735619654", "author: "David West"} |
Кроме того, есть объект класса Library , который ожидает, что объект типа Book будет передан его методу register() :
|
1
2
3
4
5
|
class Library { public void register(Book book) { // Create a new record in the database }} |
Скажем также, что тип Book имеет простой метод isbn() :
|
1
2
3
|
interface Book { String isbn();} |
Теперь вот точка входа HTTP (я использую Takes and Cactoos ), которая принимает запрос POST multipart/form-data и регистрирует книгу в библиотеке:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public class TkUpload implements Take { private final Library library; @Override public Response act(Request req) { String body = new RqPrint( new RqMtSmart(new RqMtBase(req)).single("book") ).printBody(); JsonObject json = Json.createReader( new InputStreamOf(body) ).readObject(); Book book = new BookDTO(); book.setIsbn(json.getString("isbn")); library.register(book); }} |
Что не так с этим? Ну, несколько вещей.
Во-первых, это не для повторного использования. Если бы нам понадобилось нечто подобное в другом месте, нам пришлось бы снова написать эту обработку HTTP и анализ JSON.
Во-вторых, обработка ошибок и валидация также не подлежат повторному использованию. Если мы добавим его в метод выше, нам придется копировать его везде. Конечно, DTO может инкапсулировать это, но это не то, для чего обычно DTO.
В-третьих, приведенный выше код является довольно процедурным и имеет много временных связей .
Лучшим дизайном было бы скрыть этот разбор внутри нового класса JsonBook :
|
01
02
03
04
05
06
07
08
09
10
11
12
|
class JsonBook implements Book { private final String json; JsonBook(String body) { this.json = body; } @Override public String isbn() { return Json.createReader( new InputStreamOf(body) ).readObject().getString("isbn"); }} |
Тогда точка входа RESTful будет выглядеть так:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public class TkUpload implements Take { private final Library library; @Override public Response act(Request req) { library.register( new JsonBook( new RqPrint( new RqMtSmart(new RqMtBase(req)).single("book") ).printBody() ) ); }} |
Разве это не элегантнее?
Вот несколько примеров из моих проектов: RqUser из zerocracy / farm и RqUser из yegor256 / jare .
Как видно из приведенных выше примеров, иногда мы не можем использовать implements потому что некоторые примитивы в Java — это не интерфейсы, а final классы: String — «идеальный» пример. Вот почему я должен сделать это:
|
1
2
3
4
5
6
|
class RqUser implements Scalar<String> { @Override public String value() { // Parsing happens here and returns String }} |
Но помимо этого, эти примеры прекрасно демонстрируют принцип «разбора объектов», предложенный выше.
| Опубликовано на Java Code Geeks с разрешения Егора Бугаенко, партнера нашей программы JCG . См. Оригинальную статью здесь: не анализируйте, используйте синтаксический анализ объектов
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |
