Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php DDD—访问存储库中实体的状态_Php_Design Patterns_Domain Driven Design - Fatal编程技术网

Php DDD—访问存储库中实体的状态

Php DDD—访问存储库中实体的状态,php,design-patterns,domain-driven-design,Php,Design Patterns,Domain Driven Design,我觉得我缺乏良好设计的经验,可能会使事情过于复杂,如果我这样做了,请告诉我:) 让我们看一个用户实体和用户存储库的示例。 我将从存储库开始 class UserRepository { public function save(User $user) { if($user->getStatus() == User::STATUS_NEW) $this->getDataAccessObject()->insert($user->getState())

我觉得我缺乏良好设计的经验,可能会使事情过于复杂,如果我这样做了,请告诉我:)

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

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继承的抽象存储库和抽象实体)

  • 我放弃了工厂对象/类模式,而是更喜欢在实体内部使用工厂方法模式

  • 在本例中,factory方法看起来是多余的,可以替换为

    $user=UserEntity(数组('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;
      }
    }
    
    我觉得它“更正确”,因为在这里我不公开实体的内部状态,只有存储库知道它

    那你觉得呢?实体的内部状态是否正常?或者第二个例子是设计这种系统的更好方法?或者你知道更好的方法

    多谢各位

    这些方法只能在存储库中使用

    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);
      }
    }
    
    如果需要,请使每个实体和存储库本身具有相同的基类,而实体getter/setter是其受保护的成员。这样做是受保护的,不再公开

    这将使存储库和实体更加紧密地结合在一起,这在您的情况下可能并不坏,因为存储库产生了实体

    这些方法只能在存储库中使用

    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);
      }
    }
    
    如果需要,请使每个实体和存储库本身具有相同的基类,而实体getter/setter是其受保护的成员。这样做是受保护的,不再公开


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

    首先:不要对实体和存储库使用单一基类;它们没有关系,你这样做会打破每一条坚实的原则:)

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

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

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

    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();
    }
    
    现在这看起来可能和hakre说的一样,但是在Repository模式和DAO模式之间有很大的区别

    如果我对PHP中的成员可见性选项有所了解(比如,除了public/private/protected?internal之外,您还有其他作用域吗?)

    编辑:我刚想到我在方法签名中给出了一些误导性的信息;一个DAO不应该返回一个实体,一个实体应该有一个构造函数获取一个DAO,并从中创建一个新的DAO


    编辑2:另一种方法是将UserEntity视为值对象。不要为国家公开一个setter,而是公开一个getter。存储库使用它获取数据,并在检索实体时通过构造函数简单地传递所有属性。但是,这会对如何使用用户对象产生一些影响,这也是我之前没有建议的原因。

    首先:不要对实体和存储库使用单个基类;它们没有关系,你这样做会打破每一条坚实的原则:)

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

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

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