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)
}
Использована иллюстрация с сайта Мартина Фаулера.