在课堂上拥有相当数量的公共财产是否合理?

时间:2010-02-08 21:40:00

标签: php class refactoring class-design

或者更具体地说,不依赖于制定者和吸气者是否“好”?

我正在处理一个检查房间可用性的类,并设置有十几个公共属性。事情如:

  • unitNumber
  • roomTypes(array)
  • codeCorporate
  • 代码组
  • numberKids
  • numberAdults
  • numberRooms
  • 货币
  • minRate
  • MAXRATE
  • SoapServer的
  • units(array)
  • hotelId

在实例化对象之后,在各种方法中使用$this->设置这些属性。但是,处理对象的代码通常直接设置公共属性,而不是使用getter / setter方法:

$object->something = 3;

foreach ($object->things as $thing ) { }

如果我有时间重构这个课程..

  • 我应该将所有这些属性都保存在属于私有属性的数据数组中,并定义__set__get方法吗?
  • 我应该为每个属性制作一个getter方法吗?

5 个答案:

答案 0 :(得分:3)

我个人还没有找到公共财产的真正理由,尽管我愿意接受建议:-)

虽然我更喜欢为每个属性指定getter / setter(无论是否为广义get($name)的代理)。我假设您已经有其他代码使用直接赋值,所以在这种情况下我会说继续使用魔术__get/__set方法。

答案 1 :(得分:3)

在我看来,任何公众成员都不是一个好主意。它增加了类之间的耦合,并使重构非常复杂(如果需要它。)

Setters / Getters是你要走的路,你付出的非常小的性能损失通常要么被优化掉,要么被优雅所打破。

要回答关于数组与单个get-get-per-var的问题,这是一个品味问题。我倾向于只保留数组中类似类型的变量,并将其余部分分开。

答案 2 :(得分:2)

我认为大多数人会建议使用setter&干将。现在你只能设置&获取属性,但是如果要在访问该属性时记录该怎么办?或者您可能希望首先通过验证函数运行该值(电子邮件,电子邮件,邮政编码等)。也许你需要调用另一个函数,或者设置另一个属性。我想你会看到我要走向何方。通过使用setter& getters,你为你的课程添加了一个宝贵的封装层,99%的时候这值得你需要做的额外打字;)想象一下,如果没有setter& amp;干将。至少可以说这是一个令人头痛的问题。

编辑:我忘了提到Doctrine。它是一个对象关系映射器(ORM),可以自动设置setter&吸气剂给你(除其他外)。您可以在http://www.doctrine-project.org/

查看

答案 3 :(得分:1)

我会后退一步并提出一些更普遍的问题:

  • 为什么我要披露这么多信息;什么在使用它以及为什么?
  • 这个类真的只是一个没有行为的数据结构,在这种情况下应该是其他类的私有类吗?
  • 这个课程是出于单一目的,还是成为单一的目的?

您可能会发现您可以创建类实例的视图以导出到数据库,以表单形式显示等。首先查看“构建器”和“非循环访问者”模式。

关于访问者,我认为不需要将它们用于您所描述的内容:检索类属性和内部状态信息,即结构。但是,对于类的属性,我可以在某些情况下看到好处,但更多的是检索属性,而不是对象状态的突变。

答案 4 :(得分:1)

如果我可以在几个月后添加我的盐:

拥有公共财产是非常好的。所有内容都应该被封装,因为(除其他原因外)使用直接属性操作并不能为您提供在某些外部源修改字段时轻松重构或执行(更多)控制检查的方法。例如,假设您有一个包含许多字段的类,这些字段在整个项目中多次使用,并且该项目包含数千个文件;这是一个已经运行并扩展了几年的项目。假设公司正在改变它的商业模式,或者发现某些字段的数据类型存在问题,现在需要进行一些验证;您是否会在直接访问公共成员的所有数千个源代码中复制该验证?在PHP中,解决方案可能很简单,但在大多数OO编程语言(例如Java)中都不行。事实是OO是based on encapsulation.简而言之,封装不仅产生干净的代码,而且还具有高度可维护性(更不用说具有成本效益和凝聚力)的代码。

您对__get / __set操纵私有成员(数组)的建议很好。这样,如果你需要一些额外的验证,只需创建你的setter和/或你的getter,它就会结束。有些人可能会因为代码完成无法在__get / __set上启用而产生反效果。恕我直言,依靠代码完成简直是懒惰的编码。但话说回来,让每个成员拥有自己的getter和/或setter都可以让你编写更全面的API文档。就个人而言,我通常将技术用于内部或非常通用的类。如果您的所有字段都不需要任何验证,或者您说了几十个字段,那么在我看来,使用魔术方法是可以接受的。

底线是避免直接成员访问类实例,句点。您如何决定实现这一目标完全取决于您。只需确保API记录得更清楚,就可以了解它。

最后一点,在PHP中,如果您已经使用了未封装其字段的类,例如

class SomeObject {
   public $foo;
   public $bar;
   public $baz;
   //...
}

您可以简单地修复此类,而无需使用以下内容重构任何内容:

class SomeObject {
   private $_foo;   // having underscore as prefix helps to know what's private/protected
   private $_bar;   // inside the code.
   private $_baz;

   public function __get($name) {
      $methodName = 'get'.ucfirst($name);
      if (method_exists($this, $methodName)) {
         return $this->{$methodName}();
      } else {
         throw new Exception("Method '{$methodName}' does not exist");
      }
   }

   public function __set($name, $value) {
      $methodName = 'set'.ucfirst($name);
      if (method_exists($this, $methodName)) {
         $this->{$methodName}($value);
      } else {
         throw new Exception("Method '{$methodName}' does not exist");
      }
   }

   public function getFoo() { return $this->_foo; }
   public function setFoo($value) { $this->_foo = $value; }

   public function getBar() { return $this->_bar; }
   public function setBar($value) { $this->_bar = $value; }

   public function getBaz() { return $this->_baz; }
   public function setBaz($value) { $this->_baz = $value; }

}

然后

$obj = new SomeObject();
$obj->foo = 'Hello world';    // legacy code support
$obj->setFoo('Hello world');  // same thing, but preferred

您同时满足OO范例和直接访问到实例的属性。您也可以 __call() 检查前缀'get'或'set'并相应地调用 __get() __set() ,但我不会那么做,尽管这样可以真正实现通用课程通过->member->getMember() / ->setMember()

访问其私人会员