代码示例在PHP中,但问题与语言无关。
我试图找出将服务层分成多个明确定义的图层的最佳方法。
在下面的示例中,我上传了base64编码的用户头像,并展示了它将如何通过图层。我使用装饰器模式来模拟图层。
重要:
传递到每一层的数据通常会在传递到下一层之前以某种方式更改,这正是我正在寻找的。我不喜欢的一件事是,为了更新头像,您必须先与ValidatedProfile
对象交谈,而不是说Profile
个对象。关于它的一些东西看起来很奇怪,但我总是有一个Profile
对象可以将调用委托给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);
}
}
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);
}
}
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.
}
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 :(得分:0)
我认为你的图层太多了。对于像这样的操作,两个主要对象应该足够了。您需要验证头像输入并以某种方式持久化。
由于您需要验证用户ID并且它以某种方式与持久性相关联,因此您可以将其委托给UserService对象。
interface UserService {
/**
* @param string $userId
* @param resource $imageResource
*/
public function updateAvatar($userId, $imageResource);
/**
* @param string $userId
* @return bool
*/
public function isValidId($userId);
}
检查有效用户ID应该是请求验证的一部分。我不会像验证一样单独执行。所以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);
}
}
}