Data Transfer Object (Объект передачи данных)
Паттерн проектирования Data Transfer Object
Описание Data Transfer Object
Объект, который пересылает данные между процессами для уменьшения количества вызовов методов.
При работе с удалённым интерфейсом, таким как, например, Remote Facade, каждый запрос к нему достаточно затратен. В результате, приходится уменьшать количество вызовов, что означает необходимость передачи большего количества данных за один вызов. Чтобы реализовать это, как вариант, можно использовать множество параметров. Однако, при этом зачастую код получается неуклюжим и неудобным. Также это часто невозможно в таких языках, как Java, которые возвращают лишь одно значение.
Решением здесь является паттерн Data Transfer Object, который может хранить всю необходимую для вызова информацию. Он должен быть сериализуемым для удобной передачи по сети. Обычно используется объект-сборщик для передачи данных между DTO и объектами в приложении.
В сообществе Sun многие используют термин "Value Object" для обозначения этого паттерна. Мартин Фаулер подразумевает под этим термином ( Value Object ) несколько иной паттерн. Обсуждение этого можно прочесть в его книге P of EEA на странице 487.
Примеры реализации
// Data Transfer Object Pattern in JavaScript
class UserDTO {
constructor(data = {}) {
this.id = data.id || null;
this.name = data.name || '';
this.email = data.email || '';
this.age = data.age || 0;
this.address = data.address || '';
}
toJSON() {
return {
id: this.id,
name: this.name,
email: this.email,
age: this.age,
address: this.address
};
}
static fromJSON(json) {
return new UserDTO(json);
}
static fromUser(user) {
return new UserDTO({
id: user.id,
name: user.name,
email: user.email,
age: user.age,
address: user.address
});
}
}
class User {
constructor(id, name, email, age, address) {
this.id = id;
this.name = name;
this.email = email;
this.age = age;
this.address = address;
}
}
// Usage
const user = new User(1, 'John Doe', 'john@example.com', 30, '123 Main St');
const userDTO = UserDTO.fromUser(user);
// Serialize for network transfer
const jsonData = JSON.stringify(userDTO.toJSON());
console.log('Serialized DTO:', jsonData);
// Deserialize from network
const receivedData = JSON.parse(jsonData);
const receivedDTO = UserDTO.fromJSON(receivedData);
console.log('Received DTO:', receivedDTO);
// Data Transfer Object Pattern in C++
#include <iostream>
#include <string>
#include <map>
class UserDTO {
private:
int id;
std::string name;
std::string email;
int age;
std::string address;
public:
UserDTO() : id(0), age(0) {}
UserDTO(int id, const std::string& name, const std::string& email,
int age, const std::string& address)
: id(id), name(name), email(email), age(age), address(address) {}
// Getters
int getId() const { return id; }
std::string getName() const { return name; }
std::string getEmail() const { return email; }
int getAge() const { return age; }
std::string getAddress() const { return address; }
// Setters
void setId(int id) { this->id = id; }
void setName(const std::string& name) { this->name = name; }
void setEmail(const std::string& email) { this->email = email; }
void setAge(int age) { this->age = age; }
void setAddress(const std::string& address) { this->address = address; }
// Serialization
std::map<std::string, std::string> toMap() const {
std::map<std::string, std::string> data;
data["id"] = std::to_string(id);
data["name"] = name;
data["email"] = email;
data["age"] = std::to_string(age);
data["address"] = address;
return data;
}
static UserDTO fromMap(const std::map<std::string, std::string>& data) {
UserDTO dto;
dto.id = std::stoi(data.at("id"));
dto.name = data.at("name");
dto.email = data.at("email");
dto.age = std::stoi(data.at("age"));
dto.address = data.at("address");
return dto;
}
};
class User {
private:
int id;
std::string name;
std::string email;
int age;
std::string address;
public:
User(int id, const std::string& name, const std::string& email,
int age, const std::string& address)
: id(id), name(name), email(email), age(age), address(address) {}
UserDTO toDTO() const {
return UserDTO(id, name, email, age, address);
}
};
// Data Transfer Object Pattern in Go
package main
import (
"encoding/json"
"fmt"
)
type UserDTO struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
Address string `json:"address"`
}
func (dto *UserDTO) ToJSON() ([]byte, error) {
return json.Marshal(dto)
}
func (dto *UserDTO) FromJSON(data []byte) error {
return json.Unmarshal(data, dto)
}
type User struct {
ID int
Name string
Email string
Age int
Address string
}
func (u *User) ToDTO() *UserDTO {
return &UserDTO{
ID: u.ID,
Name: u.Name,
Email: u.Email,
Age: u.Age,
Address: u.Address,
}
}
func NewUserFromDTO(dto *UserDTO) *User {
return &User{
ID: dto.ID,
Name: dto.Name,
Email: dto.Email,
Age: dto.Age,
Address: dto.Address,
}
}
// Usage
func main() {
user := &User{
ID: 1,
Name: "John Doe",
Email: "john@example.com",
Age: 30,
Address: "123 Main St",
}
dto := user.ToDTO()
// Serialize for network transfer
jsonData, err := dto.ToJSON()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Serialized DTO:", string(jsonData))
// Deserialize from network
var receivedDTO UserDTO
err = receivedDTO.FromJSON(jsonData)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Received DTO: %+v\n", receivedDTO)
}
# Data Transfer Object Pattern in Python
import json
from dataclasses import dataclass, asdict
from typing import Optional
@dataclass
class UserDTO:
id: Optional[int] = None
name: str = ''
email: str = ''
age: int = 0
address: str = ''
def to_dict(self):
return asdict(self)
def to_json(self):
return json.dumps(self.to_dict())
@classmethod
def from_dict(cls, data):
return cls(**data)
@classmethod
def from_json(cls, json_str):
data = json.loads(json_str)
return cls.from_dict(data)
@classmethod
def from_user(cls, user):
return cls(
id=user.id,
name=user.name,
email=user.email,
age=user.age,
address=user.address
)
class User:
def __init__(self, user_id, name, email, age, address):
self.id = user_id
self.name = name
self.email = email
self.age = age
self.address = address
def to_dto(self):
return UserDTO.from_user(self)
# Usage
if __name__ == "__main__":
user = User(1, "John Doe", "john@example.com", 30, "123 Main St")
dto = user.to_dto()
# Serialize for network transfer
json_data = dto.to_json()
print("Serialized DTO:", json_data)
# Deserialize from network
received_dto = UserDTO.from_json(json_data)
print("Received DTO:", received_dto)
<?php
// Data Transfer Object Pattern in PHP
class UserDTO {
private $id;
private $name;
private $email;
private $age;
private $address;
public function __construct($data = []) {
$this->id = $data['id'] ?? null;
$this->name = $data['name'] ?? '';
$this->email = $data['email'] ?? '';
$this->age = $data['age'] ?? 0;
$this->address = $data['address'] ?? '';
}
public function toArray() {
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'age' => $this->age,
'address' => $this->address
];
}
public function toJSON() {
return json_encode($this->toArray());
}
public static function fromArray($data) {
return new self($data);
}
public static function fromJSON($json) {
$data = json_decode($json, true);
return new self($data);
}
public static function fromUser($user) {
return new self([
'id' => $user->getId(),
'name' => $user->getName(),
'email' => $user->getEmail(),
'age' => $user->getAge(),
'address' => $user->getAddress()
]);
}
// Getters
public function getId() { return $this->id; }
public function getName() { return $this->name; }
public function getEmail() { return $this->email; }
public function getAge() { return $this->age; }
public function getAddress() { return $this->address; }
}
class User {
private $id;
private $name;
private $email;
private $age;
private $address;
public function __construct($id, $name, $email, $age, $address) {
$this->id = $id;
$this->name = $name;
$this->email = $email;
$this->age = $age;
$this->address = $address;
}
public function toDTO() {
return UserDTO::fromUser($this);
}
// Getters
public function getId() { return $this->id; }
public function getName() { return $this->name; }
public function getEmail() { return $this->email; }
public function getAge() { return $this->age; }
public function getAddress() { return $this->address; }
}
// Usage
$user = new User(1, "John Doe", "john@example.com", 30, "123 Main St");
$dto = $user->toDTO();
// Serialize for network transfer
$jsonData = $dto->toJSON();
echo "Serialized DTO: " . $jsonData . "\n";
// Deserialize from network
$receivedDTO = UserDTO::fromJSON($jsonData);
echo "Received DTO: " . $receivedDTO->toJSON() . "\n";
?>
Использована иллюстрация с сайта Мартина Фаулера.