Repository (Репозиторий)

Паттерн проектирования Repository

Паттерн проектирования Repository

Описание Repository

Посредничает между уровнями области определения и распределения данных (domain and data mapping layers), используя интерфейс, схожий с коллекциями для доступа к объектам области определения.

Система со сложной моделью области определения может быть упрощена при помощи дополнительного уровня, например Data Mapper, который бы изолировал объекты от кода доступа к БД. В таких системах может быть полезным добавление ещё одного слоя абстракции поверх слоя распределения данных (Data Mapper), в котором бы был собран код создания запросов. Это становится ещё более важным, когда в области определения множество классов или при сложных, тяжелых запросах. В таких случаях добавление этого уровня особенно помогает сократить дублирование кода запросов.

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

Примеры реализации

// Repository Pattern in JavaScript
class User {
    constructor(id, name, email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
}

class UserRepository {
    constructor() {
        this.users = new Map();
    }
    
    findById(id) {
        return this.users.get(id);
    }
    
    findAll() {
        return Array.from(this.users.values());
    }
    
    save(user) {
        this.users.set(user.id, user);
        return user;
    }
    
    delete(id) {
        return this.users.delete(id);
    }
    
    findByEmail(email) {
        return this.findAll().find(user => user.email === email);
    }
}

// Usage
const userRepo = new UserRepository();
const user = new User(1, 'John Doe', 'john@example.com');
userRepo.save(user);

const foundUser = userRepo.findById(1);
const allUsers = userRepo.findAll();
// Repository Pattern in C++
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>

class User {
private:
    int id;
    std::string name;
    std::string email;
    
public:
    User(int id, const std::string& name, const std::string& email)
        : id(id), name(name), email(email) {}
    
    int getId() const { return id; }
    std::string getName() const { return name; }
    std::string getEmail() const { return email; }
};

class UserRepository {
private:
    std::vector<std::unique_ptr<User>> users;
    
public:
    User* findById(int id) {
        auto it = std::find_if(users.begin(), users.end(),
            [id](const std::unique_ptr<User>& user) {
                return user->getId() == id;
            });
        return (it != users.end()) ? it->get() : nullptr;
    }
    
    std::vector<User*> findAll() {
        std::vector<User*> result;
        for (auto& user : users) {
            result.push_back(user.get());
        }
        return result;
    }
    
    void save(std::unique_ptr<User> user) {
        users.push_back(std::move(user));
    }
    
    bool remove(int id) {
        auto it = std::find_if(users.begin(), users.end(),
            [id](const std::unique_ptr<User>& user) {
                return user->getId() == id;
            });
        if (it != users.end()) {
            users.erase(it);
            return true;
        }
        return false;
    }
};
// Repository Pattern in Go
package main

import "fmt"

type User struct {
    ID    int
    Name  string
    Email string
}

type UserRepository interface {
    FindByID(id int) (*User, error)
    FindAll() ([]*User, error)
    Save(user *User) error
    Delete(id int) error
    FindByEmail(email string) (*User, error)
}

type InMemoryUserRepository struct {
    users map[int]*User
}

func NewInMemoryUserRepository() *InMemoryUserRepository {
    return &InMemoryUserRepository{
        users: make(map[int]*User),
    }
}

func (r *InMemoryUserRepository) FindByID(id int) (*User, error) {
    user, exists := r.users[id]
    if !exists {
        return nil, fmt.Errorf("user not found")
    }
    return user, nil
}

func (r *InMemoryUserRepository) FindAll() ([]*User, error) {
    users := make([]*User, 0, len(r.users))
    for _, user := range r.users {
        users = append(users, user)
    }
    return users, nil
}

func (r *InMemoryUserRepository) Save(user *User) error {
    r.users[user.ID] = user
    return nil
}

func (r *InMemoryUserRepository) Delete(id int) error {
    delete(r.users, id)
    return nil
}

func (r *InMemoryUserRepository) FindByEmail(email string) (*User, error) {
    for _, user := range r.users {
        if user.Email == email {
            return user, nil
        }
    }
    return nil, fmt.Errorf("user not found")
}
# Repository Pattern in Python
from abc import ABC, abstractmethod
from typing import List, Optional

class User:
    def __init__(self, id: int, name: str, email: str):
        self.id = id
        self.name = name
        self.email = email

class UserRepository(ABC):
    @abstractmethod
    def find_by_id(self, id: int) -> Optional[User]:
        pass
    
    @abstractmethod
    def find_all(self) -> List[User]:
        pass
    
    @abstractmethod
    def save(self, user: User) -> User:
        pass
    
    @abstractmethod
    def delete(self, id: int) -> bool:
        pass
    
    @abstractmethod
    def find_by_email(self, email: str) -> Optional[User]:
        pass

class InMemoryUserRepository(UserRepository):
    def __init__(self):
        self.users = {}
    
    def find_by_id(self, id: int) -> Optional[User]:
        return self.users.get(id)
    
    def find_all(self) -> List[User]:
        return list(self.users.values())
    
    def save(self, user: User) -> User:
        self.users[user.id] = user
        return user
    
    def delete(self, id: int) -> bool:
        if id in self.users:
            del self.users[id]
            return True
        return False
    
    def find_by_email(self, email: str) -> Optional[User]:
        for user in self.users.values():
            if user.email == email:
                return user
        return None

# Usage
if __name__ == "__main__":
    repo = InMemoryUserRepository()
    user = User(1, "John Doe", "john@example.com")
    repo.save(user)
    
    found_user = repo.find_by_id(1)
    all_users = repo.find_all()
<?php
// Repository Pattern in PHP
class User {
    private $id;
    private $name;
    private $email;
    
    public function __construct($id, $name, $email) {
        $this->id = $id;
        $this->name = $name;
        $this->email = $email;
    }
    
    public function getId() { return $this->id; }
    public function getName() { return $this->name; }
    public function getEmail() { return $this->email; }
}

interface UserRepository {
    public function findById($id);
    public function findAll();
    public function save(User $user);
    public function delete($id);
    public function findByEmail($email);
}

class InMemoryUserRepository implements UserRepository {
    private $users = [];
    
    public function findById($id) {
        return isset($this->users[$id]) ? $this->users[$id] : null;
    }
    
    public function findAll() {
        return array_values($this->users);
    }
    
    public function save(User $user) {
        $this->users[$user->getId()] = $user;
        return $user;
    }
    
    public function delete($id) {
        if (isset($this->users[$id])) {
            unset($this->users[$id]);
            return true;
        }
        return false;
    }
    
    public function findByEmail($email) {
        foreach ($this->users as $user) {
            if ($user->getEmail() === $email) {
                return $user;
            }
        }
        return null;
    }
}

// Usage
$repo = new InMemoryUserRepository();
$user = new User(1, "John Doe", "john@example.com");
$repo->save($user);

$foundUser = $repo->findById(1);
$allUsers = $repo->findAll();
?>

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

Источник