OOP最佳实践(特别是PHP)

时间:2011-06-01 07:43:40

标签: php oop

我已经在PHP中开发了一段时间,但直到最近才转向OOP方法。

一个不断出现的问题是“与OOP的关系有多远”,特别是在执行速度和内存资源等方面。

例如,假设我有2个对象,User和Listing

列表始终链接到单个用户。 UserId是Listing的一个属性,所以我知道它与哪个用户有关。 偶尔,在Listing方法中,我需要访问相关User的单个属性。

据我所知(如果没有,请提供建议)我有3种方法可以解决这个问题。

  1. 通过$ user - >创建一个新的用户对象并访问相关的属性。利人

  2. 将required属性设置为Listing的local属性,并在初始化Listing时填充它(例如,通过sql join)

  3. 直接查询数据库以通过用户ID

  4. 检索用户所需的属性

    在我看来,选项1& 2更严格地遵守OOP规则,但由于初始化整个对象只是为了检索1个属性而具有性能命中。选项3将是内存密集程度最低但完全避开OOP。

    此外,在创建时填充对象方面,我的大多数对象在初始化后不久就会通过一个“填充”方法填充其大部分属性(因此只需要1个DB查询)。这通常被认为是最佳做法,还是更可取的是使用单独的方法来获取这些属性,并在需要时填充?

    我意识到可能没有“正确”的答案,但任何人都可以就如何处理这种情况提出建议吗?

    非常感谢 尼克

4 个答案:

答案 0 :(得分:2)

我绝对更喜欢选项1.选项2不遵循OOP背后的想法,因为用户信息不属于您的列表,因此请将其保存在单独的对象中。

牢记以下规则:

  1. 一次加载没有关系的对象。
  2. 如果您需要相关对象,则根据规则1
  3. 在您需要时加载它

    许多ORM以这种方式工作,例如Doctrine(http://www.doctrine-project.org)。它被称为延迟加载。

    当你需要一个对象的一个​​属性时,你会发现自己正在加载同一个对象的第二个属性。在许多情况下,您会发现自己在一个完整的脚本中只为一个对象进行了大量的数据库查询。

答案 1 :(得分:1)

选项1是首选的OOP方式。

或者,在php中你可以写一个包装器对象,它只包含user_id和你需要的属性。

当调用方法或访问其他属性时,您可以加载并启动真实的用户对象。

这可以通过__get(),__ set()和__call()方法来实现。

这样,您可以优化Listing对象的查询和内存使用,同时在需要时可以访问用户对象的所有OOP优点。

代码应该看起来像:

class LazyObject {

  private 
    $propertySubset = array(),
    $realObject;


 function __call($method, $args) {
    if ($this->realObject === null) {
      $this->initRealObject();
    }
    return call_user_func_array(array($this->realObject, $method), $args);
  }

  // __get, __set and initRealObject implementation goes here

}

警告

  • 您可以使用引用:myMethod(&$ref)$shortcut = &$object->prop无法使用
  • 您需要手动检查是否为包装器填充了足够的属性。 没有足够的属性会导致分配查询,而包装器只会减慢速度。
  • 除非在lazyObject
  • 中实现它们,否则接口(如ArrayAccess)的使用将不起作用

PS。
这应被视为最佳化黑客。因此,只有在性能成为问题时才实现这一点;

答案 2 :(得分:1)

在您急于选择#1之前,我建议您继续尝试将简单对象模型应用于存储在数据库中的数据可能会带来很多复杂情况。

请阅读本文以了解我所说的内容: "Why did object oriented databases fail"

你说尼克,没有“正确”的答案。但我们可以这样说:

在OOP理想的良好实践中,$ listing对象应该具有$ listing->用户属性,该属性是表示列表用户的对象。为了获得更好的性能,只要实例化$ listing,就不应该实例化此User对象。在Java传统编码中,您将拥有一个方法 - > getUser()。在PHP中,只要您调用 - > user属性,就可以实例化此对象。

但是当您只需要一个属性时检索完整的用户信息,这可能会花费很多无用的数据流。如果您的代码打算一次处理大量的Listing对象,则尤其如此。

如果是这样,那么您必须从理想的对象模型中稍微移开一点,以便更接近您的业务流程。然后只检索您需要的用户属性,这可能是一个很好的妥协。例如:$ listing-> getUserProp1()。如果您将来可能需要其他用户属性,则可以将方法更改为$ listing-> getUserProprty('prop1')。

但是,只有在有时需要User属性时,此选项才有用。如果实际上每次有一个Listing对象时都需要它,那么最好用其他Listing属性初始化User属性。这是你的选择#2。它似乎是一个非常好的解决方案,您需要最小化数据流,并且如果您不需要在Listing对象附近的其他用户信息。

答案 3 :(得分:0)

我相信您需要了解数据传输对象设计模式:

视图(页面)应该接收数据传输对象,这些对象不能与数据访问层一起使用。

通过一些参数化,您的业务层可能会返回DTO,这些DTO具有由数据访问层返回的一个或多个对象的特定集合。

例如,如果您想列出用户,可能您想知道他们的“组标识符”,“用户标识符”和“全名”。

用户组的标识符来自某个Group对象,而用户的标识符和全名来自某个User对象,最后,UserDto对象将具有User和Group对象的属性,这些属性将是某些用户界面视图所需的属性。显示用户列表。