Шаблон декоратора для имитации многослойного сервисного слоя

Примеры кода на PHP, но вопрос не зависит от языка.

ситуация

Я пытаюсь найти лучший способ разделить сервисный слой на несколько четко определенных слоев.

В приведенном ниже примере я загружаю аватар пользователя в кодировке base64 и показываю, как он будет проходить через слои. Я использую шаблон декоратора для имитации слоев.

Важный:
Данные, передаваемые на каждый слой, обычно перед тем, как переходить на следующий уровень, обычно каким-то образом изменяются, и это именно то, что я ищу. Единственное, что мне не нравится в этом, это то, что для обновления аватара вы должны сначала поговорить с ValidatedProfile объект вместо сказать Profile объект. Что-то в этом кажется странным, но я всегда мог иметь Profile объект, который делегирует вызовы ValidatedProfile,

Слои

  1. Проверка:
    Здесь вы проверяете данные. Как и в примере ниже, именно здесь вы проверяете формат строки $ avatar и проверяете, что это допустимый ресурс изображения. Во время процесса проверки часто создаются объекты и ресурсы сущностей, которые затем передаются на следующий уровень.
  2. Проверка:
    Выполните проверки, такие как проверка подлинности предоставленного идентификатора. Как и в приведенном ниже примере, здесь я проверяю, является ли предоставленный идентификатор пользователя действительным идентификатором пользователя.
  3. командующий:
    Где происходит действие, которое должно быть выполнено. К тому времени, когда этот уровень достигнут, считается, что данные полностью проверены и проверены, и дальнейшие проверки на нем не требуются. Командир делегирует действие другим службам (обычно службам сущностей), а также может вызывать другие службы для выполнения дополнительных действий.
  4. Сущность:
    Этот уровень работает с действиями, которые должны быть выполнены над объектом и / или его отношениями.

ValidatedProfile

class ValidatedProfile
{
private $verifiedProfile;

/**
* @param string $avatar Example: data:image/png;base64,AAAFBfj42Pj4
*/
public function updateAvatar($userId, $avatar)
{
$pattern = '/^data:image\/(png|jpeg|gif);base64,([a-zA-Z0-9=\+\/]+)$/';
if (!preg_match($pattern, $avatar, $matches)) {
// error
}

$type = $matches[1]; // Type of image
$data = $matches[2]; // Base64 encoded image data

$image = imagecreatefromstring(base64_decode($data));
// Check if the image is valid etc...

// Everything went okay
$this->verifiedProfile->updateAvatar($userId, $image);
}
}

VerifiedProfile

class VerifiedProfile
{
private $profileCommander;

public function updateAvatar($userId, $image)
{
$user = // get user from persistence
if ($user === null) {
// error
}

// User does exist
$this->profileCommander->updateAvatar($user, $image);
}
}

ProfileCommander

class ProfileCommander
{
private $userService;

public function updateAvatar($user, $image)
{
$this->userService->updateAvatar($user, $image);

// If any processes need to be run after an avatar is updated
// you can invoke them here.
}

UserService

class UserService
{
private $persistence;

public function updateAvatar($user, $image)
{
$fileName = // generate file name

// Save the image to disk.

$user->setAvatar($fileName);

$this->persistence->persist($user);
$this->persistence->flush($user);
}
}

Вы могли бы тогда иметь Profile класс как следующий:

class Profile
{
private $validatedProfile;

public function updateAvatar($userId, $avatar)
{
return $this->validatedProfile->updateAvatar($userId, $avatar);
}
}

Таким образом, вы просто говорите с экземпляром Profile скорее, чем ValidatedProfile что имеет больше смысла, я думаю.

Есть ли лучшие и более общепринятые способы достижения того, что я пытаюсь сделать здесь?

0

Решение

Я думаю, что у вас слишком много слоев. Для такой операции должно хватить двух основных объектов. Вам нужно проверить ввод аватара и как-то сохранить его.

Поскольку вам необходимо проверить идентификатор пользователя и он каким-то образом связан с постоянством, вы можете делегировать его объекту UserService.

interface UserService {

/**
* @param string $userId
* @param resource $imageResource
*/
public function updateAvatar($userId, $imageResource);

/**
* @param string $userId
* @return bool
*/
public function isValidId($userId);
}

Эта проверка на действительный идентификатор пользователя должна быть частью проверки запроса. Я бы не стал делать отдельный шаг вроде проверки. Таким образом, UserAvatarInput может справиться с этим (реализация валидации является лишь примером) и небольшим методом-оберткой, чтобы сохранить все это.

class UserAvatarInput {

/**
* @var UserService
*/
private $userService;

/**
* @var string
*/
private $userId;

/**
* @var resource
*/
private $imageResource;

public function __construct(array $data, UserService $service) {
$this->userService = $service; //we need it for save method
$errorMessages = [];

if (!array_key_exists('image', $data)) {
$errorMessages['image'] = 'Mandatory field.';
} else {
//validate and create image and set error if not good
$this->imageResource = imagecreatefromstring($base64);
}

if (!array_key_exists('userId', $data)) {
$errorMessages['userId'] = 'Mandatory field.';
} else {
if ($this->userService->isValidId($data['userId'])) {
$this->userId = $data['userId'];
} else {
$errorMessages['userId'] = 'Invalid user id.';
}
}

if (!empty($errorMessages)) {
throw new InputException('Input Error', 0, null, $errorMessages);
}
}

public function save() {
$this->userService->updateAvatar($this->userId, $this->imageResource);
}

}

Я использовал объект исключения для передачи сообщений проверки.

class InputException extends Exception {

private $inputErrors;

public function __construct($message, $code, $previous, $inputErrors) {
parent::__construct($message, $code, $previous);
$this->inputErrors = $inputErrors;
}

public function getErrors() {
return $this->inputErrors;
}

}

Вот как клиент будет использовать это, например:

class UserCtrl {

public function postAvatar() {
try {
$input = new AvatarInput($this->requestData(), new DbUserService());
$input->save();
} catch (InputException $exc) {
return new JsonResponse($exc->getErrors(), 403);
}
}

}
0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]