Dto java что это
Перейти к содержимому

Dto java что это

  • автор:

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

Fork me on GitHub

Объект, который пересылает данные между процессами для уменьшения количества вызовов методов.

При работе с удалённым интерфейсом, таким как, например, Remote Facade, каждый запрос к нему достаточно затратен. В результате, приходится уменьшать количество вызовов, что означает необходимость передачи большего количества данных за один вызов. Чтобы реализовать это, как вариант, можно использовать множество параметров. Однако, при этом зачастую код получается неуклюжим и неудобным. Также это часто невозможно в таких языках, как Java, которые возвращают лишь одно значение.

Решением здесь является паттерн Data Transfer Object , который может хранить всю необходимую для вызова информацию. Он должен быть сериализуемым для удобной передачи по сети. Обычно используется объект-сборщик для передачи данных между DTO и объектами в приложении.

В сообществе Sun многие используют термин «Value Object» для обозначения этого паттерна. Мартин Фаулер подразумевает под этим термином ( Value Object ) несколько иной паттерн. Обсуждение этого можно прочесть в его книге P of EEA на странице 487.

Использована иллюстрация с сайта Мартина Фаулера.

  • Главная
  • Список паттернов
  • Сайт создан и поддерживается Василием Кулаковым.

Основы проектирования и дизайна

Данный модуль является чисто теоретическим. Практическое задание в него не включено.

Возможно вы заметили, что приложения прошлых практик имеют схожую структуру. В этом разделе мы попытаемся объяснить, почему и для чего это сделано, попутно проанализировав их составные части.

Любое приложение работает с какими-либо данными: одни обрабатывают платежи, другие показывают доступные товары в интернет-магазине, а третьи выполняют поисковые запросы, составленные пользователем.
Во всех этих случаях у нас есть бизнес-домен — банк, интернет-магазин или поисковая система.

Бизнес-домен — набор объектов и отношений между ними, которые характеризуют какую-либо предметную область из реального мира.

Пример: Банковская система (упрощенно).
Сущности: Пользователь, организация, счет, платеж, депозит, карта.
Связи: Пользователь принадлежит организации. У пользователя может быть много счетов. Организация делает платеж другой организации. Пользователь открывает депозит. Пользователь заводит карту.

Entity

Сущности в контексте приложения обычно называются Entity.

Entity — сущность из бизнес-домена, представленная в нашей программе как класс, в котором есть только поля, геттеры и сеттеры. Никакая логика не должна содержаться в этом классе.

Пример Entity — сущность ипотечного займа.
Она используется при сохранении и получении данных из базы через слой Repository. Пример:

package ru.bank.abc.entity; /** * Ипотечный займ (упрощенный вариант) */ public class Mortgage  /** * Продукт (например Семейная ипотека) */ private Product product; /** * Размер заемных средств */ private BigDecimal amount; /** * Размер ипотечной ставки */ private BigDecimal interestRate; /** * Срок кредитования */ private Integer period; /** * Список продавцов */ private ListSeller> sellers; /** * Список заявителей */ private ListApplicant> applicants; /** * Объект недвижимости */ private Realty realty; public Product getProduct()  return product; > public void setProduct(Product product)  this.product = product; > public BigDecimal getAmount()  return amount; > //. геттеры и сеттеры для полей public Realty getRealty()  return realty; > public void setRealty(Realty realty)  this.realty = realty; > > 

DTO

Также в приложениях выделяют сущности DTO (Data-transfer object).

Data Transfer Object (DTO) — один из шаблонов проектирования, который используется для передачи данных между подсистемами приложения.

Технически, это то же самое, что и Entity, но с точки зрения бизнес-домена, эти объекты никак не связаны с ней.

Например, несколько полей, объединенных в одну сущность (DTO) для отправки в сервис расчета графика платежей. Пример:

package ru.bank.abc.dto; /** * Запрос для расчета графика платежей */ public class PaymentScheduleCalculationRequest  /** * Размер заемных средств */ private BigDecimal amount; /** * Размер ипотечной ставки */ private BigDecimal interestRate; /** * Срок кредитования */ private Integer period; public BigDecimal getAmount()  return amount; > public void setAmount(BigDecimal amount)  this.amount = amount; > public BigDecimal getInterestRate()  return interestRate; > public void setInterestRate(BigDecimal interestRate)  this.interestRate = interestRate; > public Integer getPeriod()  return period; > public void setPeriod(Integer period)  this.period = period; > > 

Также DTO могут использоваться для слоя отображения.
То есть, это сущность, которая будет возвращаться пользователю в браузер (или в любое другое приложение-клиент) для отображения.

Как правило, нет необходимости в отображении полной сущности со всеми данными одновременно на стороне клиента. Поэтому данные возвращаются в урезанном виде, который ровно совпадает с тем, что пользователь должен увидеть в текущий момент на своем экране.

Например, если пользователь открыл форму для просмотра данных об участниках сделки, нет необходимости возвращать всю сущность ипотеки целиком, достаточно лишь вернуть DTO сущность, содержащую поля со списками продавцов List sellers и заявителей List applicants .

Service

Какие же сущности отвечают за выполнение бизнес логики в нашем приложении?

Такие классы именуются как Service (Сервис).

Service — класс, который отвечает за бизнес-логику при работе с конкретным Entity или DTO.

Как правило для каждой сущности Entity создается свой Service.
Через него может проходить логика создания\получения\обновления\удаления сущностей (CRUD операции), если такая предусмотрена в приложении.

Например для сущности Mortgage это будет MortgageService .
Он будет содержать CRUD методы, в рамках которых может выполняться дополнительная логика, например проверка корректности данных при создании нового ипотечного займа.

С уровня Service мы также можем обращаться к другим классам Service уровня.

Например при создании новой сущности Mortgage у нас возникает необходимость в расчете процентной ставки BigDecimal interestRate .
Для этого мы должны обратиться из класса MortgageService в другой класс-сервис — ExternalEvaluationService который в свою очередь сформирует специальную DTO, которую отправит (через сеть) в совершенно другое приложение, которое произведет расчет и вернет нам обратно результат через ExternalEvaluationService в MortgageService и который мы запишем в нашу сущность Mortgage в поле BigDecimal interestRate .

public interface MortgageService  boolean isUserFits(User user); boolean createMortgageRequest(Mortgage mortgage); Mortgage changeRate(Mortgage old); > 

Repository (DAO)

Также с уровня Service мы можем обращаться на уровень Repository (Репозиторий).
Данный слой необходим для того, чтобы сохранять в базу или получать из базы какие-либо данные.

Repository — класс, который взаимодействует с каким-либо физическим хранилищем (например База данных) для наших объектов.

Здесь опять же используется аналогичный подход — для каждой сущности Entity создается свой Repository.

Например для сущности Mortgage это будет MortgageRepository .

В чем же разница? Приведем пример из реальной жизни: мы пришли в банк и хотим положить 10 рублей на наш счет. Мы подходим к кассе и говорим операционисту наш счет, ФИО и деньги. Она пересчитывает деньги, сверяет данные и кладет деньги в кассу. С точки зрения бизнесс-домена, оператор это сервис, а касса, с которой она взаимодействует это репозиторий. Заметим следующие особенности:

  • Сервис выполняет валидацию данных и, если воникла ошибка, говорит её.
  • Сервис обращается к репозиторию, у которого есть свой интерфейс и внутренняя логика, скрытая от сервиса.
  • Если мы захотим поменять репозиторий (способ работы с деньгами), то логика работы сервиса для конечного пользователя (нас) останется неизменной, что придает гибкости нашей программе.
  • Сервис может выполнять какие-то дополнительные функции, которые нужны для нашего бизнес-процесса. Например, при успешном зачислении денег операционист выдает чек-квитанцию.

Так

Важно понимать, что при переносе такой модели в программный код, все реалзации классов должны лежать в своих пакетах, которые соответствуют их сущности: entity , dto , service , repostory . Например:

Иногда слой хранения данных делят на 2 паттерна:

  • DAO (Data Access Object)
  • рассмотренный выше Repository

Они оба отвечают за взаимодействие с каким-то хранилищем сущностей. В чем же тогда заключается разница между ними?

Представим ситуацию, что мы реализуем хранение сущности User . В случае с UserRepository у нас был бы следующий интерфейс:

public interface UserRepository  User get(String userName); void create(User user); void update(User user); void delete(String userName); ListUser> query(UserSpecification specification); // Один метод что правит всеми запросами поиска > 

А в случае с UserDAO такой:

public interface UserDAO  void create(User user); void update(User user); void delete(User user); ListUser> getUserByLastName(String lastName); User getUserByAgeRange(int minAge, int maxAge); // Еще миллион методов поиска с разными параметрами > 

Наверное, вы уже уловили суть. Самое главное отличие Repository от DAO в том, что он инкапсулируют всевозможные варианты поиска сущности в какой-то объект UserSpecification. В нем могут содержаться различные комбинации полей, по которым идет поиск, сколько страниц сущностей мы хотим увидеть и в каком порядке (сортировка).

Так

В общем случае наша программа будет выглядеть следующим образом (Разве что у нас нет базы данных):

Чистый код

Для того чтобы хорошо написать программу, недостаточно просто знать, на какие классы разделить функционал. Важно знать и понимать, как писать качественный, красивый и легкий в поддержке код.

Принцип S.O.L.I.D.

Принцип S.O.L.I.D. — это акроним из 5 основных принципов проектирования программ:

  1. Single Responsibility (Принцип единственной ответственности ) — Каждый класс должен иметь одну и только одну причину для изменений. С нарушением SRP система с трудом поддается изменениям, поскольку любое минимальное изменение вызывает эффект «снежного кома», затрагивающего другие компоненты системы.
  2. Open Closed Principle (Принцип открытости/закрытости) — Программные сущности должны быть:
    • открыты для расширения(наследования)
    • закрыты для изменения
      Идея в том, что однажды разработанная реализация класса в дальнейшем требует только исправления ошибок, а новые или изменённые функции требуют создания нового класса. Этот новый класс может переиспользовать код исходного класса через механизм наследования. Производный подкласс может реализовывать или не реализовывать интерфейс исходного класса.
  3. Liskov Substitution Principle (Принцип подстановки Барбары Лисков) — объекты в программе должны быть заменяемыми на экземпляры их подтипов без изменения правильности выполнения программы. Проще говоря, если у вас в проекте повсеместно используется экземпляры класса Collection , то вы вольны заменить их на List , Set или Queue .
  4. Interface Segregation Principle (Принцип разделения интерфейса) — много интерфейсов, специально предназначенных для клиентов, лучше, чем один интерфейс общего назначения.
  5. Dependency Inversion Principle (Принцип инверсии зависимостей) — Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. Сюда входя следующие кейсы:
    • Все типы переменных должны быть объявлены интерфейсом или абстрактным классом.
    • Все классы должны быть зависимыми только от интерфейсов других классов. ? Все инициализации переменных должны происходить через паттерн Inversion of Control — например с помощью Factory method или механизмом Dependency injection

Принцип DRY

Принцип, нацеленный на снижение повторения информации различного рода. Нацелен на уменьшение кода и упрошения связей между сущностями.

Принцип YAGNI

Принцип, при котором в качестве основной цели и/или ценности декларируется отказ от избыточной функциональности. Цель этого принципа в уменьшении количества фич, и, как следствие, количества кода, модулей и зависимостей.

Принцип KISS

Принцип, запрещающий использование более сложных решений, чем это необходимо. Он происходит от похожих концепций, таких как бритва Оккама , который гласит Не следует множить сущее без необходимости .

Основные материалы

  1. Видео: Learn — Clean Code
  2. Стиль кода Java
  3. Три ключевых принципа ПО, которые вы должны понимать

Дополнительные материалы

  1. Stackoverflow: DTO vs entity
  2. Stackoverflow: Repository vs service
  3. Habr: DAO vs Repository
  4. Книга: Роберт Мартин — Чистый код

Вопросы для самоконтроля

Java Program Structure

  1. Что такое Entity?
  2. Что такое DTO? Как расшифровывается?
  3. В чем разница между Entity и DTO?
  4. За что отвечает слой Service?
  5. За что отвечает слой Repository?
  6. Для чего нужно делить наше приложение на слои?

Java — Programming Principles

  1. Что такое принцип DRY? Как вы его понимаете?
  2. Что такое принцип KISS? Как вы его понимаете?
  3. Что такое принцип YAGNI? Как вы его понимаете?
  4. Что такое SOLID? Из каких составляющих принципов он состоит?
  5. Приведите пример нарушения принципа Liskov Substitution
  6. В чем основная цель приципа Dependency Inversion ?

Developed by Timur Sokolov and Ilia Isakhin.

We believe that knowledge is the most powerful tool you can use to change your life for better.

© 2019-2020 EPAM Systems, Izhevsk.

Переосмысление DTO в Java

Привет, Хабр! Представляю вашему вниманию любительский перевод статьи “Rethinking the Java DTO” Стивена Уотермана, где автор рассматривает интересный и нестандартный подход к использованию DTO в Java.

Я провел 12 недель в рамках программы подготовки выпускников Scott Logic, работая с другими выпускниками над внутренним проектом. И был момент, который застопорил меня больше других: структура и стиль написания наших DTO. Это вызывало массу споров и обсуждений на протяжении всего проекта, но в итоге я понял, что мне нравится использовать DTO.

Данный подход не является единственно верным решением, но он довольно интересный и отлично подходит для разработки с использованием современных IDE. Надеюсь, что изначальный шок пройдет и вам он тоже понравится.

Что такое DTO (Data Transfer Object)?

Зачастую, в клиент-серверных приложениях, данные на клиенте (слой представления) и на сервере (слой предметной области) структурируются по-разному. На стороне сервера это дает нам возможность комфортно хранить данные в базе данных или оптимизировать использование данных в угоду производительности, в то же время заниматься “user-friendly” отображением данных на клиенте, и, для серверной части, нужно найти способ как переводить данные из одного формата в другой. Конечно, существуют и другие архитектуры приложений, но мы остановимся на текущей в качестве упрощения. DTO-подобные объекты могут использоваться между любыми двумя слоями представления данных.

DTO — это так называемый value-object на стороне сервера, который хранит данные, используемые в слое представления. Мы разделим DTO на те, что мы используем при запросе (Request) и на те, что мы возвращаем в качестве ответа сервера (Response). В нашем случае, они автоматически сериализуются и десериализуются фреймворком Spring.

Представим, что у нас есть endpoint и DTO для запроса и ответа:

// Getters & Setters, конструкторы, валидация и документация опущены public class CreateProductRequest < private String name; private Double price; >public class ProductResponse < private Long id; private String name; private Double price; >@PostMapping("/products") public ResponseEntity createProduct( @RequestBody CreateProductRequest request ) < /*. */ >

Что делают хорошие DTO?

Во-первых, очень важно понимать, что вы не обязаны использовать DTO. Это прежде всего паттерн и ваш код может работать отлично и без него.

  • Если вы используете одно представление данных на оба слоя, вы вполне можете использовать ваши сущности в качестве DTO.
  • Если вы хотите вручную заниматься сериализацией ваших сущностей в JSON, то я не могу вас остановить!

Они также помогают документировать слой представления в человеко читаемом виде. Мне нравится использовать DTO и, я думаю, вы тоже могли бы их использовать, ведь это к тому же способствует уменьшению зацепления (decoupling) между слоем представления и предметным слоем, позволяя приложению быть более гибким и уменьшая сложность его дальнейшей разработки.

Тем не менее, не все DTO являются хорошими. Хорошие DTO помогают создавать API согласно лучшим практикам и в соответствии с принципам чистого кода.

Они должны позволять разработчикам писать API, которое внутренне согласовано. Описание параметра на одной из конечных точек (endpoint) должно применяться и к параметрам с тем же именем на всех связанных точках. В качестве примера, возьмём вышепредставленный фрагмент кода. Если поле price при запросе определено как “цена с НДС”, то и в ответе определение поля price не должно измениться. Согласованное API предотвращает ошибки, которые могли возникнуть из-за различий между конечными точками, и в то же время облегчает введение новых разработчиков в проект.

DTO должны быть надёжными и сводить к минимуму необходимость в написании шаблонного кода. Если при написании DTO легко допустить ошибку, то вам нужно прилагать дополнительные усилия, чтобы ваше API оставалось согласованным. DTO должны “легко читаться”, ведь даже если у нас есть хорошее описание данных из слоя представления — оно будет бесполезно, если его тяжело найти.

Давайте посмотрим на примеры DTO, а потом определим, соответствуют ли они нашим требованиям.

Покажи нам код!

Этот код на первый взгляд может показаться довольно странным, но не переживайте. В оставшейся части поста я объясню почему я остановился на данной реализации и какие преимущества она дает. Надеюсь, что вам станет всё понятно и вы оцените данный подход.

Он частично основывается на реальном коде из нашего проекта для выпускников, переведенный в контекст интернет-магазина. В нём каждый продукт имеет название, розничную и оптовую цену. Для хранения цены мы используем тип данных Double, но в реальных проектах вы должны использовать BigDecimal.

public enum ProductDTO private interface Name < @NotBlank String getName(); >private interface Price < @Positive Double getPrice(); >private interface Cost < @Positive Double getCost(); >public enum Request > public enum Response @Value public static class Private implements Id, Name, Price, Cost < Long id; String name; Double price; Double cost; >> >

Мы создаем по одному файлу для каждого контроллера, который содержит базовый enum без значений, в нашем случае это ProductDTO. Внутри него, мы разделяем DTO на те, что относятся к запросам (Request) и на те, что относятся к ответу (Response). На каждый endpoint мы создаем по Request DTO и столько Response DTO сколько нам необходимо. В нашем случае у нас два Response DTO, где Public хранит данные для любого пользователя и Private который дополнительно содержит оптовую цену продукта.

Для каждого параметра мы создаем отдельный интерфейс с таким же именем. Каждый интерфейс содержит один-единственный метод — геттер для параметра, который он определяет. Любая валидация осуществляется через метод интерфейса. В нашем примере, аннотация @NotBlank проверяет что название продукта в DTO не содержит пустую строку.

Для каждого поля который входит в DTO мы реализовываем соответствующий интерфейс. В нашем случае аннотация @Value из библиотеки Lombok делает это за нас, автоматически генерируя геттеры.

Для полного сравнения, с использованием документации, вы можете посмотреть на примеры до и после. Также необходимо понимать, что это небольшие примеры и разница становится более наглядной как только вы начнете добавлять больше DTO.

“Это ужасно!”

Это на самом деле выглядит странно и здесь много необычных моментов. Давайте обсудим несколько из них подробнее.

Три enum и ни один из них не имеет значений! На самом деле мы используем небольшую хитрость для создания namespace-а, т.е. мы можем обращаться к DTO как ProductDTO.Request.Create . Данный “трюк” возможен благодаря тому, что мы ставим ; после каждого enum. Точка с запятой указывает на конец (пустого) списка значений! Использование таких namespace-ов ускоряет поиск нужного DTO, а также можно воспользоваться подсказками в IDE для получения полного списка. Есть и другие возможности добиться того же эффекта, но текущий подход выглядит лаконично, ведь нам не нужно будет использовать конструкции вроде new ProductDTO() и new Create() . Честно говоря, это моё личное предпочтение и вы можете организовать классы как вам угодно.

Мы используем слишком много интерфейсов — по одному на каждый параметр! Мы делаем это потому что считаем данные интерфейсы единственным источником описательной информации относительно параметра который он определяет. Далее мы поговорим об этом чуть больше, но поверьте мне, это принесет свои плоды.

Мы не реализовали методы интерфейсов. Да, выглядит немного странно и я хотел бы найти решение получше. Сейчас мы используем автогенерацию геттеров при помощи Lombok для закрытия контракта и это небольшой хак. Выглядело бы лучше, если бы мы могли объявлять поля сразу в интерфейсе, что позволяло бы создавать DTO в одной строчке кода. Однако, в java нет возможности интерфейсам иметь не статические поля. Если вы будете использовать этот подход в других языках, то возможно ваш код будет более лаконичным.

Это (почти) идеально

Давайте вернемся к нашим требованиям к созданию хорошего DTO. Соотвествует ли им наш подход?

Согласованный синтаксис

Мы определенно улучшили согласованность синтаксиса и это главное почему мы могли бы начать использовать данный паттерн. Каждый API параметр теперь имеет свой синтаксис, определенный через интерфейс. Если DTO содержит опечатку в имени параметра или некорректный тип — код просто не скомпилируется и IDE выдаст вам ошибку. Для примера:

@Value public static class PatchPrice implements Id, Price < String id; // Должен быть тип Long; Double prise; // Опечатка в слове price >
PatchPrice is not abstract and does not override abstract method getId() in Id PatchPrice is not abstract and does not override abstract method getPrice() in Price

К тому же, когда мы используем валидацию на уровне интерфейса, мы исключаем ситуацию, когда один и тот же параметр на одном endpoint проходит валидацию и не проходит её на другом.

Согласованная семантика

Такой стиль написания DTO улучшает понимание кода через наследование документации. Каждый параметр имеет свою семантику которая определена в геттер методах соответствующего ему интерфейса. Пример:

private interface Cost < /** * The amount that it costs us to purchase this product * For the amount we sell a product for, see the parameter. * This data is confidential */ @Positive Double getCost(); >

Как только в DTO мы реализовали данный интерфейс, наша документация автоматически стала доступна через геттер.

Теперь вы гарантировано получаете актуальную и целостную документацию во всех DTO, которые реализовали данный интерфейс. В редких случаях, когда вам нужно добавить API, параметр с уже используемым наименованием и разной семантикой, вам придется создать отдельный интерфейс. Хоть это и неудобно, но заставляет разработчиков задуматься в таких ситуациях, а будущим читателям этого кода понять разницу между схожими параметрами.

Читабельность & Поддерживаемость

Будем честны: в нашем подходе достаточно много шаблонного кода. У нас есть 4 интерфейса, без которых не обойтись, и каждый DTO имеет длинную строку с перечислением интерфейсов. Мы можем вынести интерфейсы в отдельный пакет, что поможет избежать лишних “шумов” в коде c описанием DTO. Но даже после этого, бойлерплейт остается главным недостатком данного подхода, что может оказаться веской причиной для того чтобы использовать другой стиль. Для меня, эти затраты все еще стоят того.

Наш стиль проявляет себя с лучшей стороны, когда нам нужно создать новый DTO. Вы просто пишете @Value public static class [name] implements , перечисляете нужные вам интерфейсы. Далее, добавляете поля пока ваша IDE не перестанет ругаться. Готово! У вас есть DTO с валидацией и документацией.

К тому же, мы видим всю структуру наших DTO классов. Посмотрите на код и вы увидите все что вам нужно знать из сигнатуры класса. Каждое поле указано в списке реализованных интерфейсов. Достаточно нажать ctrl + q в IntelliJ и вы увидите список полей.

В нашем подходе мы пишем валидацию единоразово, т.к. она реализуется через методы интерфейса. Создали новое DTO — получили валидацию в подарок, после реализации интерфейса.

И в заключении, благодаря нашим интерфейсам, мы способны писать переиспользуемые утилитные методы. В качестве примера, рассмотрим ситуацию, когда нам нужно посчитать наценку на товар:

markup = (sale_price - cost_price) / cost_price

В java, мы можем реализовать это используя обобщение:

public static Double getMarkup(T dto)< return (dto.getPrice() - dto.getCost()) / dto.getCost(); >

Входной аргумент имеет тип T , который является обобщением с пересечением типов. dto обязано реализовать оба интерфейса Price и Cost — это означает, что мы не можем использовать данный метод для Public ответа (т.к. он не реализовывает интерфейс Cost ). В стандартном подходе, мы должны были бы добавить метод с двумя аргументами и перегруженные методы для каждого dto (пример). Это переносит работу на вызывающую сторону и добавляет риски возникновения ошибок.

Вывод

Я не жду, что вы сразу же пойдете переписывать все ваши DTO. Но есть несколько деталей которые вы можете почерпнуть для себя:

  1. Установите единственный источник информации о вашем API параметре.
  2. Маленькие интерфейсы лучше.
  3. Попробуйте быть странным, возможно, вам понравится!

P.S. Спасибо, что дочитали до конца мой первый пост на Хабре. Буду рад любой критике относительно перевода, т.к. приходилось немного отходить от оригинала из-за нехватки знаний и опыта.

Что такое 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)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *