Традиционный способ интеграции объектно-ориентированного бэкэнда с внешней системой — через объекты передачи данных , которые сериализуются в 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, являются их собственными. |