【问题标题】:Decorator pattern to simulate multil-layer service layer装饰器模式模拟多层服务层
【发布时间】:2016-01-25 20:25:03
【问题描述】:

代码示例使用 PHP,但问题与语言无关。

情况

我正在尝试找出将服务层分成多个定义明确的层的最佳方法。

在下面的示例中,我上传了一个 base64 编码的用户头像,并展示了它如何通过图层。我正在使用装饰器模式来模拟图层。

重要: 传递到每一层的数据通常会在传递到下一层之前以某种方式进行更改,这正是我正在寻找的。我不喜欢的一件事是,为了更新头像,您必须首先与ValidatedProfile 对象交谈,而不是说Profile 对象。它看起来很奇怪,但我总是可以拥有一个 Profile 对象,它将调用委托给 ValidatedProfile

层次

  1. 验证: 这是您验证数据的地方。在下面的示例中,您可以检查 $avatar 字符串的格式并确保它是有效的图像资源。在验证过程中,通常会创建实体对象和资源,然后将其传递到下一层。
  2. 验证: 执行检查,例如验证提供的 ID 是否真实。如下例所示,我在这里检查提供的用户 ID 是否实际上是用户的真实 ID。
  3. 指挥官: 要执行的操作发生在哪里。当到达这一层时,数据被认为是完全验证和验证的,不需要对其进行进一步的检查。指挥官将操作委托给其他服务(通常是实体服务),也可以调用其他服务执行更多操作。
  4. 实体: 该层处理要对实体和/或其关系执行的操作。

验证配置文件

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,我认为这更有意义。

有没有更好和更广泛接受的方法来实现我在这里尝试做的事情?

【问题讨论】:

    标签: java php oop model-view-controller service-layer


    【解决方案1】:

    我认为你的层太多了。对于这样的操作,两个主要对象应该足够了。您需要验证头像输入并以某种方式保留它。

    由于您需要验证用户 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);
            }
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2020-06-09
      • 2012-11-26
      • 2016-05-19
      • 2011-04-19
      • 1970-01-01
      • 1970-01-01
      • 2021-12-10
      • 2016-08-18
      • 1970-01-01
      相关资源
      最近更新 更多