2011-06-01 135 views
15

我一直在PHP开发一段时间,但直到最近才切换到OOP方法。这一直此起彼伏,我面向对象的最佳实践(特别是PHP)

的一个问题是“有多远”去与面向对象的东西,特别是在执行速度方面和内存资源等

例如,假设我有2个对象,用户和列表

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

据我所见(请告知如果没有的话)我有3个选项可以实现这个功能。

  1. 创建一个新的用户对象,并通过$用户访问培训相关属性格式 - >利人

  2. 进行必要的财产清单的本地属性和填充此房源被初始化时(例如,通过SQL JOIN )

  3. 查询数据库直接检索用户的通过用户ID所需要的属性

它似乎对我来说,选项1更加严格地遵守OOP规则,但由于初始化整个对象只是为了检索1个属性而具有性能命中。选项3将是最少的内存密集型,但完全避开了OOP。另外,就创建时填充对象而言,我的大多数对象在初始化后不久(通过只需要1个DB查询)就通过一个“填充”方法获得大部分属性。这通常会被认为是最佳实践,还是最好使用单独的方法来获得这些属性,并在需要时填充?

我意识到这可能没有“正确”的答案,但任何人都可以提供有关处理这种情况的最佳方法的建议吗?

非常感谢 尼克

+0

你可以一举(1 + 2)尽快为他们登录(假设登录),填充它,并把它存储为对象(S)获取有关用户的一切,那么就保持它周围,以获取有关信息直到他们注销然后销毁该对象。我想这就是我应该去做的。 – robx 2011-06-01 07:48:51

+2

除非你异形这三种方法,找到一个signigicantly阻碍性能,你不应该现在操心内存消耗和处理速度。只要使用有用的东西。 – Gordon 2011-06-01 07:49:40

+0

如何使User对象成为Listing对象的属性,而不仅仅是UserId? – prasopes 2011-06-01 07:50:12

回答

2

我肯定更喜欢选项1选项2不跟在别人后面OOP的想法,因为用户信息是不是你的上市的一部分,所以把它放在一个单独的对象。

请记住以下规则记:

  1. 加载对象没有其关系的一次。
  2. 如果您需要相关对象加载它在你按照规则1

许多奥姆斯以这种方式工作需要的时间,例如学说(http://www.doctrine-project.org)。它被称为延迟加载。

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

+0

但有一点意见:只有在不确定需要链接对象时才使用延迟加载。如果你已经知道你会需要它们,那么一次性加载它们 - 当然不是所有的,只是你知道你需要的那些。对于报告(如打印报告),我主要使用直接查询或视图,因为它们中的大多数需要汇总结果。 – wimvds 2011-06-01 08:51:55

+0

感谢大家提供的有用评论 - 懒加载对我来说是一个全新的概念,我将着眼于探索。我认为我在这里学到的主要观点是专注于首先维护正确的面向对象的实践,然后根据性能问题在后面应用其他技术,例如延迟加载。 – Nick 2011-06-01 09:11:53

+0

贬低我的人可以给出一个理由。我愿意听到你的论点。 – 2011-06-01 13:11:17

0

我相信你需要看一看到数据传输对象的设计模式:

视图(页)应该接受它必须是不一样的那些数据传输对象你的数据访问层是如何工作的。

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

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

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

1

选项1是首选的OOP方式。

另外,在PHP中,你可以编写一个包装对象,它只包含user_id和你需要的属性。

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

这可以实现__get(),__set()和__call()方法。

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

代码看起来应该沿着线的东西:

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不会工作
  • 你需要手动检查你是否填写了t他包装了足够的属性。 没有足够的属性会导致查询的分配,并且包装只会减慢查询速度。除非您在lazyObject实现它们

PS接口(如ArrayAccess接口)

  • 用法将不起作用。
    这应该被认为是一种最佳化黑客攻击。所以只有在性能成为问题时才实施这一点;

  • 1

    在您选择第一个选项之前,我会建议您继续尝试将简单的对象模型应用于存储在数据库中的数据,这可能会带来很多麻烦。

    为了以防读这有什么,我告诉一个想法: "Why did object oriented databases fail"

    中有你说两句,没有一个“正确”的答案。但是,我们可以这样说:

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

    但是,当你只需要一个属性中获取完整的用户信息,这可能会花费太多的无用数据流。如果您的代码想要一次处理大量的清单对象,情况尤其如此。

    如果是这样,那么你必须移动的方式与理想对象模型一点点,以便更加贴近您的业务流程。然后只检索您需要的用户资产,这可能是一个很好的折中方案。例如:$ listing-> getUserProp1()。如果将来可能需要其他用户属性,则可以将该方法更改为$ listing-> getUserProprty('prop1')。

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