Data Transfer Object (Объект передачи данных)

Объект, который пересылает данные между процессами для уменьшения количества вызовов методов.
При работе с удалённым интерфейсом, таким как, например, Remote Facade, каждый запрос к нему достаточно затратен. В результате, приходится уменьшать количество вызовов, что означает необходимость передачи большего количества данных за один вызов. Чтобы реализовать это, как вариант, можно использовать множество параметров. Однако, при этом зачастую код получается неуклюжим и неудобным. Также это часто невозможно в таких языках, как Java, которые возвращают лишь одно значение.
Решением здесь является паттерн Data Transfer Object , который может хранить всю необходимую для вызова информацию. Он должен быть сериализуемым для удобной передачи по сети. Обычно используется объект-сборщик для передачи данных между DTO и объектами в приложении.
В сообществе Sun многие используют термин «Value Object» для обозначения этого паттерна. Мартин Фаулер подразумевает под этим термином ( Value Object ) несколько иной паттерн. Обсуждение этого можно прочесть в его книге P of EEA на странице 487.
Использована иллюстрация с сайта Мартина Фаулера.
- Главная
- Список паттернов
- Сайт создан и поддерживается Василием Кулаковым.
Что такое DTO в Java?
Допустим вы пишите игру шашки. И у вас есть класс Checker. У этого класса будут только поля(цвет, дамка, и т.п.) и геттеры/сеттеры. Вот вам и DTO. Ну и конструктор.
12 дек 2018 в 7:20
Тогда какой смысл в слове Transfer? Он же должен передавать данные по объектам? Как это написано тут DTO
12 дек 2018 в 7:20
@Teemitze DTO переводится как «объект, передающий данные». Данные, которые он передает — это и есть поля.
12 дек 2018 в 7:21
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Объект Customer — DTO.
DTO объект — объект, который не содержит методы. Он может содержать только поля, геттеры/сеттеры, и конструкторы.
Data Transfer Object — объект, передающий данные. Данные — это и есть поля в классе.
Реальный пример — игра шашки. У вас должен быть объект Checker (шашка). У него не должно быть методов, только поля.
public class Checker < private COLOR checkerColor; private Coordinate coordinate; //show checker coordinate private boolean isQueen; //show is the checker queen public Checker(COLOR checkerColor, int xCoordinate, int yCoordinate) < this.checkerColor = checkerColor; coordinate = new Coordinate(xCoordinate, yCoordinate); isQueen = false; >public Checker() <> public COLOR getColor() < return checkerColor; >public Coordinate getCoordinate() < return coordinate; >public boolean isQueen() < return isQueen; >public void setCoordinate(Coordinate coordinate) < this.coordinate.setCoordinates(coordinate.getX(), coordinate.getY()); >public void setQueen() < isQueen = true; >>
Или класс Cell (шашечное поле).
public class Cell < private boolean isBusy; //shows does the field is occupied with the checker private Coordinate coordinate; //show field coordinate public Cell(boolean isBusy, int x, int y) < coordinate = new Coordinate(x, y); this.isBusy = isBusy; >public Cell() <> public boolean isBusy() < return isBusy; >public void setBusy(boolean isBusy) < this.isBusy = isBusy; >public Coordinate getCoordinate() < return coordinate; >>
Или класс Board (доска):
public class Board < private Listcells; //list with 64 Fields() private List checkers; //list with Checkers(), whose number falls from 24 to 0 public Board() < cells = new LinkedList<>(); checkers = new LinkedList<>(); > public List getCells() < return cells; >public List getCheckers() < return checkers; >>
Или класс Coordinate . Хотя у него есть методы(переопределенный equals и compare ), но это методы из Object и он тоже может считаться DTO объектом, т.к. он сделан только для того, что бы хранить данные(координаты).
public class Coordinate < private int x; //x coordinate private int y; //y coordinate public Coordinate(int x, int y) < this.x = x; this.y = y; >public int getX() < return x; >public void setX(int x) < this.x = x; >public int getY() < return y; >public void setY(int y) < this.y = y; >public void setCoordinates(int x, int y) < this.x = x; this.y = y; >@Override public boolean equals(Object o) < if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Coordinate that = (Coordinate) o; return x == that.x && y == that.y; >public boolean compare(Coordinate that, int xMove, int yMove)
Зачем и как использовать Объекты передачи данных в Laravel

Data Transfer Object (Объект передачи данных) — паттерн программирования, который, при правильном использовании, может улучшить качество код и удобство его дальнейшего сопровождения. В этой статье мы узнаем, почему нужно использовать DTO и как это сделать в Laravel.
Что такое объект передачи данных?
Объект передачи данных (DTO) используется для передачи данных между уровнями приложения. Это простой объект, который содержит набор данных, обычно в форме свойств или полей, которые представляют определенный объект или концепцию в системе. Основная цель DTO — отделить различные уровни или компоненты, позволяя им взаимодействовать и обмениваться данными без необходимости знать детали реализации друг друга.
Зачем использовать DTO
И для небольших и для сложных систем существуют стандарты и паттерны, которые позволяют создавать код высокого качества, легко поддерживаемый, адаптируемый и обновляемый, и именно здесь в игру вступают DTO.
Допустим, у меня есть такой код:
class UserController extends Controller < public function store(Request $request): JsonResponse < return response()->json([ $this->service->createUser($request->all()), Response::HTTP_CREATED ]); > >
И мне нужно изменить метод createUser . Это, в целом, довольно простой код, но если бы я его создавал год назад, то и не вспомнил бы какие данные возвращает метод $request->all() .
Я мог бы отрефакторить всё это через класс Custom Request :
class UserController extends Controller < public function store(CreateUserRequest $request): JsonResponse < return response()->json([ $this->service->createUser($request->validated()), Response::HTTP_CREATED ]); > >
Но теперь возникают две другие проблемы:
— валидация теперь связана с HTTP-запросом. Если мне нужно вызвать метод createUser , то мне нужно будет снова вручную провалидировать данные.
— данные маппированы, но поскольку $request->validated() возвращает массив, то не получится принудительно указать тип данных, передаваемых методу createUser .
Если мы применим здесь паттерн DTO, то сможем решить обе вышеописанные проблемы. Будем маппировать и валидировать данные в DTO. И теперь, при вызове метода createUser откуда либо, валидация будет произведена автоматически, нужно просто передать DTO в метод.
Как использовать DTO
Как объяснялось выше, DTO могут быть простыми объектами для маппирования свойств. Таким образом, самой простой реализацией DTO было бы создание следующего класса:
class CreateUserDTO < public function __construct( private string $name, private string $email, private string $username, private string $password ) <>// Add getters, setters, validation >
Затем создаём свой DTO из Request :
class UserController extends Controller < public function store(Request $request): JsonResponse < return response()->json([ $this->service->createUser(new CreateUserDTO(. $request->all())), Response::HTTP_CREATED ]); > >
В new CreateUserDTO(. $request->all()) мы используем именованные аргументы из PHP 8 для создания экземпляра CreateUserDTO .
Таким образом, мы решаем проблемы, которые мы подробно описали в предыдущем разделе. Теперь у нас качественный код и его легко поддерживать.
Заключение
В этой статье мы узнали, что такое Объекты передачи данных, почему мы должны использовать их в наших приложениях и как их использовать в Laravel .
Если вы хотите начать использовать DTO в своих приложениях, я создал специальный пакет, который предоставляет базовый DTO-класс с валидацией и преобразованием типов, а также artisan-команду для создания DTO.
Наш Телеграм-канал — следите за новостями о Laravel.
Задать вопросы по урокам можно на нашем форуме.
Создание объектов передачи данных (DTO)
Сейчас наш веб-API предоставляет клиенту сущности базы данных. Клиент получает данные, которые сопоставляют непосредственно с таблицами базы данных. Однако это не всегда хорошая идея. Иногда требуется изменить форму данных, отправляемых клиенту. Например, можно сделать следующее:
- Удалите циклические ссылки (см. предыдущий раздел).
- Скрытие определенных свойств, которые клиенты не должны просматривать.
- Пропуск некоторых свойств, чтобы уменьшить размер полезной нагрузки.
- Плоские графы объектов, содержащие вложенные объекты, чтобы сделать их более удобными для клиентов.
- Избегайте уязвимостей с избыточной публикацией. (Обсуждение чрезмерной публикации см. в разделе Проверка модели .)
- Отделите уровень служб от уровня базы данных.
Для этого можно определить объект передачи данных (DTO). DTO — это объект, определяющий способ отправки данных по сети. Давайте посмотрим, как это работает с сущностью Book. В папке Models добавьте два класса DTO:
namespace BookService.Models < public class BookDto < public int Id < get; set; >public string Title < get; set; >public string AuthorName < get; set; >> > namespace BookService.Models < public class BookDetailDto < public int Id < get; set; >public string Title < get; set; >public int Year < get; set; >public decimal Price < get; set; >public string AuthorName < get; set; >public string Genre < get; set; >> >
Класс BookDetailDto включает все свойства из модели Book, за исключением того, что AuthorName является строкой, которая будет содержать имя автора. Класс BookDto содержит подмножество свойств из BookDetailDto .
Затем замените два метода GET в BooksController классе версиями, возвращающими DTO. Мы будем использовать оператор LINQ Select для преобразования сущностей Book в DTO.
// GET api/Books public IQueryable GetBooks() < var books = from b in db.Books select new BookDto() < Title = b.Title, AuthorName = b.Author.Name >; return books; > // GET api/Books/5 [ResponseType(typeof(BookDetailDto))] public async Task GetBook(int id) < var book = await db.Books.Include(b =>b.Author).Select(b => new BookDetailDto() < Title = b.Title, Year = b.Year, Price = b.Price, AuthorName = b.Author.Name, Genre = b.Genre >).SingleOrDefaultAsync(b => b.Id == id); if (book == null) < return NotFound(); >return Ok(book); >
Ниже приведен SQL, созданный новым GetBooks методом . Вы видите, что EF преобразует LINQ Select в инструкцию SQL SELECT.
SELECT [Extent1].[Id] AS [Id], [Extent1].[Title] AS [Title], [Extent2].[Name] AS [Name] FROM [dbo].[Books] AS [Extent1] INNER JOIN [dbo].[Authors] AS [Extent2] ON [Extent1].[AuthorId] = [Extent2].[Id]
Наконец, измените метод , PostBook чтобы он возвращал DTO.
[ResponseType(typeof(BookDto))] public async Task PostBook(Book book) < if (!ModelState.IsValid) < return BadRequest(ModelState); >db.Books.Add(book); await db.SaveChangesAsync(); // New code: // Load author name db.Entry(book).Reference(x => x.Author).Load(); var dto = new BookDto() < Title = book.Title, AuthorName = book.Author.Name >; return CreatedAtRoute("DefaultApi", new < >, dto); >
В этом руководстве мы преобразуем В DTO вручную в коде. Другой вариант — использовать библиотеку, например AutoMapper , которая обрабатывает преобразование автоматически.