将面向对象的后端与外部系统集成的传统方式是通过数据传输对象 ,这些对象在外出之前先序列化为JSON,然后在返回时反序列化。 这种方法很流行,而且是错误的。 序列化部分应该由打印机代替,我在前面已经解释过。 这是我对反序列化的看法,应该通过猜测对象来完成。
假设有一个后端入口点,应该在库中注册一本新书,并以JSON格式到达:
{"title": "Object Thinking","isbn: "0735619654","author: "David West"
}
此外,还有一个类Library
的对象,它希望将Book
类型的对象赋予其方法register()
:
class Library {public void register(Book book) {// Create a new record in the database}
}
还可以说,类型Book
有一个简单的方法isbn()
:
interface Book {String isbn();
}
现在,这里是HTTP入口点(我正在使用Takes和Cactoos ),该入口点接受POST multipart/form-data
请求并将书注册到库中:
public class TkUpload implements Take {private final Library library;@Overridepublic 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
:
class JsonBook implements Book {private final String json;JsonBook(String body) {this.json = body;}@Overridepublic String isbn() {return Json.createReader(new InputStreamOf(body)).readObject().getString("isbn");}
}
然后,RESTful入口点将如下所示:
public class TkUpload implements Take {private final Library library;@Overridepublic Response act(Request req) {library.register(new JsonBook(new RqPrint(new RqMtSmart(new RqMtBase(req)).single("book")).printBody()));}
}
那不是更优雅吗?
下面是一些例子,从我的项目: RqUser
从zerocracy /农场和RqUser
从yegor256 / jare 。
从上面的示例可以看到,有时我们不能使用implements
因为Java中的某些原语不是接口而是final
类: String
是一个“完美”的示例。 这就是为什么我必须这样做:
class RqUser implements Scalar<String> {@Overridepublic String value() {// Parsing happens here and returns String}
}
但是除此之外,这些示例完美地说明了上面建议的“解析对象”的原理。
翻译自: https://www.javacodegeeks.com/2018/03/dont-parse-use-parsing-objects.html