访问存储库中实体的状态

我觉得我缺乏良好设计的经验,我可能会让事情变得复杂,让我知道如果我做到了:)

让我们看一个用户实体和用户存储库的例子。 生病从存储库开始

class UserRepository {
  public function save(User $user) {
    if($user->getStatus() == User::STATUS_NEW)
      $this->getDataAccessObject()->insert($user->getState());
    else
      $this->getDataAccessObject()->update($user->getState());
    $user->setStatus(User::STATUS_MANAGED);
  }
}

用户自己

class UserEntity {
  const STATUS_NEW = 1;
  const STATUS_MANAGED = 2;

  private $_status = self::STATUS_NEW; 
  private $_state = array();

  public static function create($username, $password) {
    return new UserEntity(array('Username' => $username, 'Password' => $password));
  }

  public function __construct(array $state) {
    $this->_state = $state;
  }

  public function getState() {
    return $this->_state;
  }

  public function getStatus() {
    return $this->_status;
  }

  public function setStatus($status) {
    $this->_status = $status;
  }
}

在我问这个问题之前,关于代码的几点笔记:

  • 它的PHP(强硬应该很容易理解每​​个熟悉C ++ C# Java的人)

  • 为了简化示例,我省略了基类(例如UserRepository和UserEntity从中继承的抽象Repository和抽象实体)。

  • 我放弃了工厂对象/类模式,而是更喜欢在Entity中使用工厂方法模式。

  • 工厂方法在这个例子中看起来是多余的,可以替换为

    $ user = UserEntity(array('Username'=> $ username,'Password'=> $ password));

  • 但实际上,由于工厂接受“原始”数据(例如,来自POST表单)并创建所有需要的实体或值对象,然后创建一个有效的用户实体,因此实际上它更复杂一点(因此实体内的密码可能不是真实的密码而是一个包含密码散列而不是密码的值对象)。

    现在回答这个问题:

    我没有完成自我,因为我将getState()和setStatus()方法公开给了这个世界。 这些方法只能在存储库中使用,但由于它们是公开的,所以不允许我在任何地方访问实体的状态和/或修改其状态。 这又可能是对事物的反应和过度复杂化,但我觉得它不对。

    我找到的唯一“解决方案”是通过存储库传递所有内容,就像

    class Entity {
      public static function create($identity, $param1, $param2, Repository $r) {
        $state = $r->getNewState($identity, $param1, $param2);
        return new Entity($state);
      }
    
      private $_state = null;
    
      public function __construct(State $state) {
        $this->_state = $state;
      }
    
      public function getIdentity() {
        $this->_state->getIdentity();
      }
    }
    
    class Repository {
      private $_states = array();
    
      public function getNewState($identity, ...) {
        $state = new State($identity, ...);
        $this->_states[$identity] = $state;
        return $state;
      }
    
      public function save(Entity $entity) {
        $id = $entity->getIdentity();
        //maybe check if such entity is managed by this repository..
        if($this->_states[$id]->getStatus() === State::STATUS_NEW)
          $this->getDataAccessObject()->insert($this->_states[$id]->toArray());
        else
          $this->getDataAccessObject()->update($this->_states[$id]->toArray());
        $this->_states[$id]->setStatus(State::STATUS_MANAGED);
      }
    }
    
    class State {
      const STATUS_NEW = 1;
      const STATUS_MANAGED = 2;
    
      private $_state = array()
      private $_identity = 'something';
    
      public function getIdentity() {
        return $this->_state[$this->_identity];
      }
    
      public function toArray() {
        return $this->_state;
      }
    }
    

    它看起来“更正确”,因为在这里我不公开实体的内部状态,只有存储库知道它。

    所以你怎么看? 暴露实体的内部状态是否正常? 或者,也许第二个例子是设计这种系统的更好方法? 或者,也许你知道更好的方法?

    万分感谢!


    首先:不要为Entity和Repository使用单个基类; 他们没有关系,而且你会通过这样做来打破每一个固定的原则:)

    有很多不同的方法可以解决这个问题。 首先想到的是DAO,数据访问对象模式。 我注意到你在存储库实现中使用了这个术语,但我认为你有一点倒退...通常一个实体会有一个相应的DAO,它负责持久化该特定的实体。

    UserEntity myEntity = new UserEntity("username","password");
    UserDAO myDAO = new UserDAO(myEntity)
    myDao.Create();
    

    要么

    UserDAO myDAO=new UserDAO();
    UserEntity myEntity=myDAO.GetByUsername("username");
    

    在这种情况下,UserDAO和UserEntity都可以扩展DTO基类,并且可以将业务逻辑保留在DAO中的实体和持久代码中,从而消除所有字段和(颤抖)属性的重复。 实体将承担业务逻辑的单一责任,而DAO则是持久性的单一责任。 存储库模式可用于抽象存储基础架构,但使用DAO时通常不值得。

    public class UserDTO
    {
        protected bool banned;
        protected string username;
        protected string password;
    }
    
    public class UserEntity : UserDTO
    {
        public void BlockUser();
        public void ChangePassword();
    }
    
    public class UserDAO : UserDTO
    {
        private int Id;
        public void Create();
        public void Update();
        public UserEntity GetByUsername(string userName);
        public void Delete();
    }
    

    现在看起来好像是同样的事情,但是Repository模式和DAO模式之间有很大的区别。

    如果我知道PHP中有关成员可见性选项的任何信息(比如,你是否有其他作用域而不是public / private / protected?internal?),我可以给你一些关于如何使用Repository模式实现它的建议,但是DAO好像应该在这里做...

    编辑:我刚刚发现我在方法签名中提供了一些误导性信息; 一个DAO不应该返回一个实体,一个实体应该有一个构造函数接受一个DAO,从中创建一个新的...

    编辑2:一种不同的方法是将UserEntity视为一个值对象。 不要为国家暴露二传手,而要暴露一个公共获得者。 存储库使用它获取数据,并在检索实体时通过构造函数传递所有属性。 这带来了一些关于如何使用用户对象的问题,这是我之前没有提出过的原因。


    为什么要重新发明轮子? 对于你正在尝试做的事情,已经有一个名为Doctrine2的企业级库 - 具有更多的功能,易于使用,并且可以处理你在域中可能遇到的最疯狂的聚合根。

    我建议不要使用'建立家园'的解决方案,因为AR非常繁琐的制作和维护,他们花费了大量的时间,因为需要大量的手工工作。


    这些方法只能在仓库中使用

    如果你想这样做,使每个实体和存储库本身都具有相同的基类,实体getters / setters是受保护的成员。 这些做到了保护,不再公开。

    这将使存储库和实体更紧密地结合在一起,这对您的情况可能并不坏,因为存储库会生成实体。

    链接地址: http://www.djcxy.com/p/61447.html

    上一篇: Access to the state of the Entity in Repository

    下一篇: makefile: Why does this simple makefile only process the first source file?