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

Паттерн проектирования 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";
?>

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

Источник