代码之家  ›  专栏  ›  技术社区  ›  kizzx2

PHP中的值对象与关联数组

  •  36
  • kizzx2  · 技术社区  · 15 年前

    (此问题使用PHP作为上下文,但不仅限于PHP。例如,任何具有内置哈希的语言都是相关的)

    让我们看看这个例子(php):

    function makeAFredUsingAssoc()
    {
        return array(
            'id'=>1337,
            'height'=>137,
            'name'=>"Green Fred");
    }
    

    对比:

    class Fred
    {
        public $id;
        public $height;
        public $name;
    
        public function __construct($id, $height, $name)
        {
            $this->id = $id;
            $this->height = $height;
            $this->name = $name;
        }
    }
    
    function makeAFredUsingValueObject()
    {
        return new Fred(1337, 137, "Green Fred");
    }
    

    方法1当然是Terser,但是它很容易导致错误,例如

    $myFred = makeAFredUsingAssoc();
    return $myFred['naem']; // notice teh typo here
    

    当然,有人可能会说 $myFred->naem 同样会导致错误,这是事实。然而,有一个正式的班级对我来说只是感觉更僵化,但我不能真正证明这一点。

    使用每种方法的利弊是什么?人们何时应该使用哪种方法?

    11 回复  |  直到 13 年前
        1
  •  27
  •   Zack Marrapese    15 年前

    在表面之下,这两种方法是等效的。但是,在使用类时,您可以获得大多数标准的OO好处:封装、继承等。

    另外,请看以下示例:

    $arr['naem'] = 'John';
    

    是完全有效的,可能是很难找到的错误。

    另一方面,

    $class->setNaem('John');
    

    永远不会工作。

        2
  •  28
  •   Crozin    15 年前

    像这样的简单类:

    class PersonalData {
        protected $firstname;
        protected $lastname;
    
        // Getters/setters here
    }
    

    与阵列相比没有什么优势。

    1. 没有可能做错字。 $data['firtsname'] = 'Chris'; 会工作的同时 $data->setFirtsname('Chris'); 将引发错误。
    2. 类型提示:PHP数组可以包含所有内容(不包括任何内容),而定义良好的类只包含指定的数据。

      public function doSth(array $personalData) {
          $this->doSthElse($personalData['firstname']); // What if "firstname" index doesn't exist?
      }
      
      
      public function doSth(PersonalData $personalData) {
          // I am guaranteed that following method exists. 
          // In worst case it will return NULL or some default value
          $this->doSthElse($personalData->getFirstname());
      }
      
    3. 我们可以在设置/获取操作之前添加一些额外的代码,如验证或日志记录:

      public function setFirstname($firstname) {
          if (/* doesn't match "firstname" regular expression */) {
              throw new InvalidArgumentException('blah blah blah');
          }
      
      
      
      if (/* in debbug mode */) {
          log('Firstname set to: ' . $firstname);
      }
      
      
      $this->firstname = $firstname;
      
      }
    4. 我们可以利用OOP的所有优点,如继承、多态性、类型提示、封装等…
    5. 如前所述,我们的所有“结构”都可以从提供实现的某个基类继承 Countable , Serializable Iterator 接口,以便我们的结构可以使用 foreach 回路等。
    6. IDE支持。

    唯一的缺点似乎是速度。创建一个数组并对其进行操作更快。然而,我们都知道,在许多情况下,CPU时间比程序员时间便宜得多。;)

        3
  •  8
  •   kizzx2    15 年前

    经过一段时间的思考,我有了自己的答案。

    最重要的是 首选值对象 数组是 清晰 .

    考虑这个函数:

    // Yes, you can specify parameter types in PHP
    function MagicFunction(Fred $fred)
    {
        // ...
    }
    

    对战

    function MagicFunction(array $fred)
    {
    }
    

    目的更加明确。函数作者可以执行他的要求。

    更重要的是,作为用户,我可以轻松地查找 有效弗雷德的构成 . 我只需要打开 Fred.php 发现它的内在。

    呼叫者和被呼叫者之间有合同。使用值对象,可以将此约定编写为语法检查代码:

    class Fred
    {
        public $name;
        // ...
    }
    

    如果我使用数组,我只能希望我的用户阅读注释或文档:

    // IMPORTANT! You need to specify 'name' and 'age'
    function MagicFunction(array $fred)
    {
    }
    
        4
  •  5
  •   Gordon Haim Evgi    15 年前

    根据使用情况,我可能使用或。这个类的优点是,我可以像使用类型一样使用它,并在方法或任何自省方法上使用类型提示。如果我只想从一个查询或其他东西传递一些随机数据集,我可能会使用数组。所以我想只要 弗莱德 在我的模型中有特殊的意义,我会使用一个类。

    旁白:
    ValueObjects应该是不可变的。至少,如果您在域驱动设计中引用EricEvan的定义。在福勒的诗作中,价值对象不一定是不可变的(尽管有人建议这样做),但它们不应该具有同一性,这显然是 弗莱德 .

        5
  •  3
  •   Corey Ballou    15 年前

    让我向您提出这个问题:

    做错字有什么不同? $myFred['naem'] 像打字一样 $myFred->naem ?在这两种情况下仍然存在相同的问题,它们都是错误的。

    我喜欢用 (保持简单,愚蠢)当我编程的时候。

    • 如果只是从方法返回查询的子集,只需返回一个数组。
    • 如果将数据存储为 公共/私有/静态/受保护 变量在某个类中,最好将其存储为stdclass。
    • 如果稍后要将此传递给另一个类方法,您可能更喜欢 Fred 类,即 public function acceptsClass(Fred $fredObj)

    如果要用作返回值,则可以轻松创建标准类,而不是数组。在这种情况下,您可能不太关心严格的打字。

    $class = new stdClass();
    $class->param = 'value';
    $class->param2 = 'value2';
    return $class;
    
        6
  •  2
  •   Veger    15 年前

    哈希的专家:它能够处理在设计时未知的名称-值组合。

        7
  •  1
  •   Will Vousden    15 年前

    当返回值表示应用程序中的实体时,应该使用对象,因为这是OOP的目的。如果您只想返回一组不相关的值,那么就不那么清楚了。但是,如果它是公共API的一部分,那么声明的类仍然是最好的方法。

        8
  •  1
  •   Josh    15 年前

    说实话,我都喜欢。

    • 哈希数组比生成对象快得多,时间就是金钱!
    • 但是,JSON不喜欢散列数组(看起来有点像OOP OCD)。
    • 也许对于多人的项目,一个定义良好的类会更好。
    • 哈希数组可能需要更多的CPU时间和内存(一个对象有一个预定义的数量),尽管对于每个场景都很难确定。

    但真正糟糕的是想用得太多。就像我说的,JSON不喜欢哈希。哎呀,我用了一个数组。我现在要修改几千行代码。

    我不喜欢,但看来上课更安全。

        9
  •  1
  •   Steve Clay    14 年前

    正确的值对象的好处在于,没有办法实际地创建一个无效的对象,也没有办法更改一个存在的对象(完整性和“不变性”)。只使用getter和类型提示参数,就没有办法在可编译的代码中把它弄乱,这显然可以很容易地用可扩展数组做到。

    或者,您可以在公共构造函数中验证并抛出异常,但这提供了一个更温和的工厂方法。

    class Color
    {
        public static function create($name, $rgb) {
            // validate both
            if ($bothValid) {
                return new self($name, $rgb);
            } else {
                return false;
            }
        }
        public function getName() { return $this->_name; }
        public function getRgb() { return $this->_rgb; }
    
        protected function __construct($name, $rgb)
        {
            $this->_name = $name;
            $this->_rgb = $rgb;
        }
        protected $_name;
        protected $_rgb;
    }
    
        10
  •  1
  •   Sanosay    13 年前

    我用OOP语言工作了10多年。 如果你理解物体的工作方式,你会喜欢它的。 继承、多态、封装、重载是OOP的关键优势。 另一方面,当我们谈论PHP时,我们必须考虑到PHP不是一种功能齐全的面向对象语言。 例如,我们不能使用方法重载或构造函数重载(直接)。

    PHP中的关联数组是一个非常好的特性,但我认为这会损害PHP企业应用程序。 当您编写代码时,您希望获得干净和可维护的应用程序。

    另一种观点认为,您不必使用关联数组,因为您不能使用IntelliSense。

    因此,我认为如果您想编写更干净、更可维护的代码,那么在提供OOP功能时必须使用它。

        11
  •  0
  •   Parrots    15 年前

    我更喜欢硬编码的属性,就像在第二个示例中那样。我觉得它更清楚地定义了预期的类结构(以及类上所有可能的属性)。与第一个示例相反,它归结为总是记住使用相同的键名。使用第二种方法,您可以返回并查看类,只需查看文件的顶部就可以了解属性。

    你最好知道第二个是你做错了什么——如果你试着去做的话。 echo $this->doesntExist 你会得到一个错误,但是如果你试图 echo array['doesntExist'] 你不会的。

    推荐文章