Data Transfer Object Is a Shame

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

DTO ,据我所知,是ORM设计模式的基石,我简直”崇拜”它。但让我们直接来说吧:DTO只是一种耻辱,而发明它的人是错的。他的所作所为没有任何借口。

顺便说一下,据我所知,他的名字是Martin Fowler。也许他不是DTO的唯一发明者,但他使其合法化并推荐使用。但是,毫无疑问,他是错的。

面向对象编程的关键思想是隐藏数据在对象之后。这个思想有一个名字:封装。在面向对象编程中,数据不应该是可见的。对象只能访问它们封装的数据,而不能访问其他对象封装的数据。对于这个原则,没有争议—这就是面向对象编程的全部。

然而,DTO完全违背了这个原则。

让我们看一个实际的例子。假设这是一个从某个RESTful API获取JSON文档并返回DTO的服务,然后我们可以将其存储在数据库中:

Book book = api.loadBookById(123);
database.saveNewBook(book);

我猜这就是loadBookById()方法内部会发生的事情:

Book loadBookById(int id) {
  JsonObject json = /* Load it from RESTful API */
  Book book = new Book();
  book.setISBN(json.getString("isbn"));
  book.setTitle(json.getString("title"));
  book.setAuthor(json.getString("author"));
  return book;
}

我是对的吗?我敢打赌我是对的。这对我来说已经很恶心了。无论如何,让我们继续。这很可能会发生在saveNewBook()方法中(我使用纯JDBC)。

void saveNewBook(Book book) {
  Statement stmt = connection.prepareStatement(
    "INSERT INTO book VALUES (?, ?, ?)"
  );
  stmt.setString(1, book.getISBN());
  stmt.setString(2, book.getTitle());
  stmt.setString(3, book.getAuthor());
  stmt.execute();
}

这个Book是数据传输对象设计模式的一个经典例子。它只是在两段代码、两个过程之间传递数据。book对象非常笨拙。它只知道…什么也不做。实际上,它根本不是一个对象,而是一个被动且贫血的数据结构。

正确的设计是什么?有几种选择。例如,我认为这个看起来不错:

Book book = api.bookById(123);
book.save(database);

这是在bookById()函数中发生的事情:

Book bookById(int id) {
  return new JsonBook(
    /* RESTful API access point */
  );
}

这是在 Book.save() 中发生的事情:

void save(Database db) {
  JsonObject json = /* Load it from RESTful API */
  db.createBook(
    json.getString("isbn"),
    json.getString("title"),
    json.getString("author")
  );
}

如果书籍的JSON中有许多其他参数,而这些参数无法很好地作为单个createBook()方法的参数,则会发生什么情况?这样怎么样呢:

void save(Database db) {
  db.create()
    .withISBN(json.getString("isbn"))
    .withTitle(json.getString("title"))
    .withAuthor(json.getString("author"))
    .deploy();
}

有很多其他选择。但主要的观点是数据永远不会逸出对象book。一旦对象被实例化,数据对任何其他人来说都不可见或无法访问。我们只能要求我们的对象保存自己或将自己打印到某种媒介上,但我们永远无法从中获取任何数据。

DTO的概念本身就是错误的,因为它将面向对象的代码变成了过程式代码。我们有操作数据的过程,而DTO只是那些数据的容器。不要以那种方式思考,也不要这样做。

附注:DTO还有一些其他名称:业务对象,领域对象(不在DDD中),实体对象JavaBeans

Translated by ChatGPT gpt-3.5-turbo/36 on 2023-10-01 at 08:13

sixnines availability badge   GitHub stars