3

我在一个 PHP 应用程序中工作,我希望能够实例化一个对象,但我需要它只用它的一些属性来实例化,而不是全部。例如。

class User {

    public function __construct() {
        $this->user_id = 0;
        $this->name = '';
        $this->profile = array();
        //...many other members here
    }
}

每次我实例化这个对象时,它都会带来许多数据集合(例如,它会带来它的所有“配置文件”属性等等)。这有时不是想要的行为,因为假设我只需要使用用户名,为什么要在内存中保留所有其余属性?但在其他情况下,我将立即需要它们。

一些想法:

  • 我可以创建另一个扩展 User 的类,并在构造函数方法中取消设置任何不需要的属性。但是,我正在寻找一种更可重用的方式,因此我可以对我的应用程序中的其他一些对象执行相同的操作。

  • 我可以从构造方法中取出属性,但这可能会迫使我更改应用程序的核心(User 类之上的类),并更改应用程序中的许多内容。

有没有更可重用的方式,比如使用标准的中间类或某种设计模式?谢谢你。

4

3 回答 3

3

您正在寻找的关键字是eager loadinglazy loading

简而言之:

  • Eager Loading 是您目前正在做的事情:创建对象后,您将加载所有相关的对象和属性,无论需要多长时间。
  • 延迟加载则相反:您只会在需要时加载信息。(瞬间,真的是被访问了)

--

两者的(非常基本的)实现看起来像下面的示例。

//Data Model
abstract class UserModel{
    protected $userData = null;
    protected $userPosts = null;

    protected function loadUserData(){
        //do whatever required and store in $result
        $this->userData = $result;
    }

    protected function loadUserPosts(){
        //do whatever required and store in $result
        $this->userPosts = $result;
    }

    public abstract function getUserData();
    public abstract function getUserPosts();
}

//Eager Loading
class EagerUserModel extends UserModel { 
   public function __construct() {
        $this->loadUserData()
        $this->loadUserPosts();
    }

    public function getUserData(){
      return $this->userData;
    }

    public function getUserPosts(){
      return $this->userPosts;
    }
}

//Lazy Loading
class LazyUserModel extends UserModel {    

   public function __construct() {
        //do nothing
    }

    public function getUserData(){
      if ($this->userData == null){
        $this->loadUserData();
      }

      return $this->userData;
    }

   public function getUserPosts(){
      if ($this->userPosts== null){
        $this->loadUserPosts();
      }

      return $this->userPosts;
    }
}

该示例将允许两种方式。但是,如果您不想“选择”使用哪种类型,则可以在单个类中实现急切或延迟加载。

Eager Loading 的优势在于每个信息都“就在那儿”。然而,延迟加载需要更复杂的架构。要加载“UserPosts”,您可能需要有关用户的其他数据,这意味着您必须首先加载 UserData。这是您需要考虑的事情!

那么,延迟加载总是更快?

不!这就是陷阱。想象一下,你有一个有 10 个属性的类。如果您以惰性方式加载每个属性,则需要触发 10 个 SQL 查询(SELECT Name FROM user...等等SELECT email FROM user...)。以一种急切的方式执行此操作,将只允许您运行 ONE Query: Select Name, email FROM user...

您必须找到两种方法之间的平衡。异物是否紧密耦合?(即用户 <-> 组)?-> 加载渴望。外来对象是否松散耦合(用户 -> 图片 545458 上的帖子) -> 加载惰性。

另请注意,这是一个极端的例子(100% 渴望与 100% 懒惰)。在实践中,您可能想要加载一些急切的东西(用户数据、组分配)和其他懒惰的东西(评论、组权限)——您不能为每个用例创建自己的基类扩展。然而,拥有一个“BaseClass”总是一个好主意,因为它为您提供了灵活性,无论何时需要另一个实现。

于 2013-08-07T20:36:29.787 回答
3

实现它的一种方法是创建一个包含尽可能少的专业化的基类以使其工作。扩展基类并根据需要逐步添加属性/功能将允许您控制添加的内容。

这将符合普遍接受的面向对象设计模式。

于 2013-08-07T20:29:54.103 回答
0

如果您正确编写了实体,我更愿意保留它,而不用担心一些空属性,如果实体保持其一致状态缺少一些值。如果您不需要它们,请不要使用它们。如果您的脚本更复杂或更长时间运行,请注意在不再需要对象时销毁它们。

如果实体可以泛化为更常见的实体,并在以后用作其他实体的基类,并且有意义,那么就走这条路。这将是更困难的方式,因为需要重构。

您还可以使用对象组合,当用户实体仅保留主要用户数据并且其中一个值将为另一个名为 profile 的对象准备时,例如:

class User {

    public function __construct() {
        $this->user_id = 0;
        $this->name = '';
        $this->profile = null;
    }

    public function hasProfile()
    {
        return $this->profile != null;
    }
}

class Profile {

    public function __construct() {
        $this->profile_id = 0;
        $this->userPrefOne = '';
        //...other members here
    }
}

// then
$user = new User();

if ($profileDataNeeded)
{
    $user->profile = new Profile();
    $user->profile->userPrefOne = 'something';
}
于 2013-08-07T21:35:35.067 回答