Remote Facade (Парадный вход)

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

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

Описание Remote Facade

Предоставляет общий объединяющий интерфейс для набора методов объекта для улучшения эффективности сетевого взаимодействия.

Паттерн Remote Facade в объектно-ориентированной модели улучшает работу с маленькими объектами, у которых маленькие методы. Маленькие методы открывают большие возможности для контроля и изменения поведения, а также для улучшения понимания клиентом работы приложения. Одно из последствий такого "мелко-молотого" поведения в том, что обычно происходит много взаимодействий между объектами с вызовом множества методов.

В одном адресном пространстве "мелко-молотые" взаимодействия работаю хорошо, но всё меняется, когда происходит взаимодействие между процессами. Удалённые вызовы более затратны, потому что многое надо сделать: иногда данные нужно упорядочить, проверить на безопасность, пакеты должны быть маршрутизированы на свичах. Если два процесса работают на разных краях света, даже скорость света может играть роль. Тяжелая правда в том, что любые межпроцессные взаимодействия на порядок более расточительны, чем внутрипроцессные вызовы. Даже, если оба процесса работают на одной машине. Такое влияние на производительность не может быть упущено из вида, даже приверженцами ленивой оптимизации.

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

Паттерн Remote Facade представляет собой общий "Фасад" (по GoF) поверх структуры более "мелко-молотых" объектов. Ни один из этих объектов не имеет удалённого интерфейса, а Remote Facade не включает в себя никакой бизнес-логики. Всё, что делает Remote Facade - это транслирует общие запросы в набор небольших запросов к подчинённым объектам.

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

// Remote Facade Pattern in JavaScript
class UserService {
    constructor() {
        this.users = new Map();
        this.users.set(1, { id: 1, name: 'John Doe', email: 'john@example.com' });
        this.users.set(2, { id: 2, name: 'Jane Smith', email: 'jane@example.com' });
    }
    
    getUser(id) {
        console.log(`Getting user ${id}`);
        return this.users.get(id);
    }
    
    updateUser(id, data) {
        console.log(`Updating user ${id}`);
        const user = this.users.get(id);
        if (user) {
            Object.assign(user, data);
            return user;
        }
        return null;
    }
}

class EmailService {
    sendEmail(to, subject, body) {
        console.log(`Sending email to ${to}: ${subject}`);
        return { success: true, messageId: `msg_${Date.now()}` };
    }
}

class LogService {
    log(message) {
        console.log(`Logging: ${message}`);
    }
}

class UserRemoteFacade {
    constructor() {
        this.userService = new UserService();
        this.emailService = new EmailService();
        this.logService = new LogService();
    }
    
    // Coarse-grained method that combines multiple fine-grained operations
    async createUserWithNotification(userData) {
        console.log('Remote Facade: Creating user with notification');
        
        // Simulate network calls
        const user = await this.simulateNetworkCall(() => {
            const id = Date.now();
            return { id, ...userData };
        });
        
        // Log the creation
        this.logService.log(`User created: ${user.id}`);
        
        // Send welcome email
        const emailResult = await this.simulateNetworkCall(() => {
            return this.emailService.sendEmail(
                user.email, 
                'Welcome!', 
                `Welcome ${user.name}!`
            );
        });
        
        return {
            user,
            emailSent: emailResult.success,
            messageId: emailResult.messageId
        };
    }
    
    async updateUserProfile(userId, profileData) {
        console.log('Remote Facade: Updating user profile');
        
        // Get current user
        const currentUser = await this.simulateNetworkCall(() => {
            return this.userService.getUser(userId);
        });
        
        if (!currentUser) {
            throw new Error('User not found');
        }
        
        // Update user
        const updatedUser = await this.simulateNetworkCall(() => {
            return this.userService.updateUser(userId, profileData);
        });
        
        // Log the update
        this.logService.log(`User updated: ${userId}`);
        
        return updatedUser;
    }
    
    async simulateNetworkCall(operation) {
        // Simulate network delay
        await new Promise(resolve => setTimeout(resolve, 100));
        return operation();
    }
}

// Usage
const facade = new UserRemoteFacade();

// Single remote call that does multiple operations
facade.createUserWithNotification({
    name: 'Alice Johnson',
    email: 'alice@example.com'
}).then(result => {
    console.log('User creation result:', result);
});

facade.updateUserProfile(1, {
    name: 'John Smith',
    email: 'johnsmith@example.com'
}).then(result => {
    console.log('User update result:', result);
});
<?php
// Remote Facade Pattern in PHP
class UserService {
    private $users = [];
    
    public function __construct() {
        $this->users = [
            1 => ['id' => 1, 'name' => 'John Doe', 'email' => 'john@example.com'],
            2 => ['id' => 2, 'name' => 'Jane Smith', 'email' => 'jane@example.com']
        ];
    }
    
    public function getUser($id) {
        echo "Getting user $id\n";
        return $this->users[$id] ?? null;
    }
    
    public function updateUser($id, $data) {
        echo "Updating user $id\n";
        if (isset($this->users[$id])) {
            $this->users[$id] = array_merge($this->users[$id], $data);
            return $this->users[$id];
        }
        return null;
    }
}

class EmailService {
    public function sendEmail($to, $subject, $body) {
        echo "Sending email to $to: $subject\n";
        return ['success' => true, 'messageId' => 'msg_' . time()];
    }
}

class LogService {
    public function log($message) {
        echo "Logging: $message\n";
    }
}

class UserRemoteFacade {
    private $userService;
    private $emailService;
    private $logService;
    
    public function __construct() {
        $this->userService = new UserService();
        $this->emailService = new EmailService();
        $this->logService = new LogService();
    }
    
    // Coarse-grained method that combines multiple fine-grained operations
    public function createUserWithNotification($userData) {
        echo "Remote Facade: Creating user with notification\n";
        
        // Simulate network calls
        $user = $this->simulateNetworkCall(function() use ($userData) {
            $id = time();
            return array_merge(['id' => $id], $userData);
        });
        
        // Log the creation
        $this->logService->log("User created: {$user['id']}");
        
        // Send welcome email
        $emailResult = $this->simulateNetworkCall(function() use ($user) {
            return $this->emailService->sendEmail(
                $user['email'], 
                'Welcome!', 
                "Welcome {$user['name']}!"
            );
        });
        
        return [
            'user' => $user,
            'emailSent' => $emailResult['success'],
            'messageId' => $emailResult['messageId']
        ];
    }
    
    public function updateUserProfile($userId, $profileData) {
        echo "Remote Facade: Updating user profile\n";
        
        // Get current user
        $currentUser = $this->simulateNetworkCall(function() use ($userId) {
            return $this->userService->getUser($userId);
        });
        
        if (!$currentUser) {
            throw new Exception('User not found');
        }
        
        // Update user
        $updatedUser = $this->simulateNetworkCall(function() use ($userId, $profileData) {
            return $this->userService->updateUser($userId, $profileData);
        });
        
        // Log the update
        $this->logService->log("User updated: $userId");
        
        return $updatedUser;
    }
    
    private function simulateNetworkCall($operation) {
        // Simulate network delay
        usleep(100000); // 100ms
        return $operation();
    }
}

// Usage
$facade = new UserRemoteFacade();

// Single remote call that does multiple operations
$result = $facade->createUserWithNotification([
    'name' => 'Alice Johnson',
    'email' => 'alice@example.com'
]);
echo "User creation result: " . json_encode($result) . "\n";

$updateResult = $facade->updateUserProfile(1, [
    'name' => 'John Smith',
    'email' => 'johnsmith@example.com'
]);
echo "User update result: " . json_encode($updateResult) . "\n";
?>
// Remote Facade Pattern in Go
package main

import (
    "fmt"
    "time"
)

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

type UserService struct {
    users map[int]User
}

func NewUserService() *UserService {
    return &UserService{
        users: map[int]User{
            1: {ID: 1, Name: "John Doe", Email: "john@example.com"},
            2: {ID: 2, Name: "Jane Smith", Email: "jane@example.com"},
        },
    }
}

func (us *UserService) GetUser(id int) User {
    fmt.Printf("Getting user %d\n", id)
    return us.users[id]
}

func (us *UserService) UpdateUser(id int, data User) User {
    fmt.Printf("Updating user %d\n", id)
    if user, exists := us.users[id]; exists {
        user.Name = data.Name
        user.Email = data.Email
        us.users[id] = user
        return user
    }
    return User{}
}

type EmailService struct{}

func (es *EmailService) SendEmail(to, subject, body string) map[string]interface{} {
    fmt.Printf("Sending email to %s: %s\n", to, subject)
    return map[string]interface{}{
        "success":   true,
        "messageId": fmt.Sprintf("msg_%d", time.Now().Unix()),
    }
}

type LogService struct{}

func (ls *LogService) Log(message string) {
    fmt.Printf("Logging: %s\n", message)
}

type UserRemoteFacade struct {
    userService  *UserService
    emailService *EmailService
    logService   *LogService
}

func NewUserRemoteFacade() *UserRemoteFacade {
    return &UserRemoteFacade{
        userService:  NewUserService(),
        emailService: &EmailService{},
        logService:   &LogService{},
    }
}

// Coarse-grained method that combines multiple fine-grained operations
func (urf *UserRemoteFacade) CreateUserWithNotification(userData User) map[string]interface{} {
    fmt.Println("Remote Facade: Creating user with notification")
    
    // Simulate network calls
    user := urf.simulateNetworkCall(func() User {
        userData.ID = int(time.Now().Unix())
        return userData
    })
    
    // Log the creation
    urf.logService.Log(fmt.Sprintf("User created: %d", user.ID))
    
    // Send welcome email
    emailResult := urf.simulateNetworkCall(func() map[string]interface{} {
        return urf.emailService.SendEmail(
            user.Email,
            "Welcome!",
            fmt.Sprintf("Welcome %s!", user.Name),
        )
    })
    
    return map[string]interface{}{
        "user":       user,
        "emailSent": emailResult["success"],
        "messageId": emailResult["messageId"],
    }
}

func (urf *UserRemoteFacade) UpdateUserProfile(userId int, profileData User) User {
    fmt.Println("Remote Facade: Updating user profile")
    
    // Get current user
    currentUser := urf.simulateNetworkCall(func() User {
        return urf.userService.GetUser(userId)
    })
    
    if currentUser.ID == 0 {
        panic("User not found")
    }
    
    // Update user
    updatedUser := urf.simulateNetworkCall(func() User {
        return urf.userService.UpdateUser(userId, profileData)
    })
    
    // Log the update
    urf.logService.Log(fmt.Sprintf("User updated: %d", userId))
    
    return updatedUser
}

func (urf *UserRemoteFacade) simulateNetworkCall(operation func() interface{}) interface{} {
    // Simulate network delay
    time.Sleep(100 * time.Millisecond)
    return operation()
}

// Usage
func main() {
    facade := NewUserRemoteFacade()
    
    // Single remote call that does multiple operations
    result := facade.CreateUserWithNotification(User{
        Name:  "Alice Johnson",
        Email: "alice@example.com",
    })
    fmt.Printf("User creation result: %+v\n", result)
    
    updateResult := facade.UpdateUserProfile(1, User{
        Name:  "John Smith",
        Email: "johnsmith@example.com",
    })
    fmt.Printf("User update result: %+v\n", updateResult)
}

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

Источник