5

例如,我有一个用于博客系统的 ORM 模型(PHP Active Record)。我有一个post模型,可以存储喜欢的数量。post可以是 apicturequote(比如说),它们是不同的表(因此是模型)。

架构是 a与 a或 apost一起保存共享数量、喜欢、描述等数据。picturequote

因此,在为模型编写吸气剂时,post我必须编写

public function getX() {
    if ($this->isPicture()) {
       return $this->picture->getX();
    }
    else if ($this->isQuote()) {
       return $this->quote->getX()
    }
    else {
       return self::DEFAULT_X
    }
}

我目前不得不为许多吸气剂编写这种结构。我可以做些什么来避免这种情况吗?

PS:标记为 PHP 因为那是我的代码。

编辑

  • 将注释更改为代码。
  • 这是一个模型(以及数据库中的相应表),它具有比 apicture和更多的数据quote。例如,description这是 的一部分,post并且不位于picture或 上quote
  • pictures 和quotes 的表格。
  • 使用 PHP Active Record,这三个类中的每一个都扩展了 PHP Active Record 提供的通用模型类。
  • picture模型有它自己的数据。对quote.
4

2 回答 2

2

扩展评论中提到的策略模式的想法:

class Post {
    // get the correct 'strategy'
    public function getModel() {
        if ($this->isPicture()) {
            return $this->picture;
        }

        if ($this->isQuote()) {
            return $this->quote;
        }

        return null;
    }

    // using the strategy
    public function getX() {
        $model = $this->getModel();

        if (null === $model) {
            return self::DEFAULT_X;
        }

        return $model->getX();
    }
}

Post每个策略都可能实现与暴露这些 getter 的类相同的接口。更好的是提供一个默认策略(而不是返回 null)并让它返回默认值。这样,每个 getter 中的空检查就变得多余了。

于 2012-04-27T00:04:59.997 回答
0

另一种方法是元编程的一种非常基本的形式。这个想法是你比手动调用你的方法更高,让代码为你做这件事。

(假设方法定义都是 的一部分Post

public function getX($model = null) {
   if ($model) return $model->getX();
   else return self::DEFAULT_X;
}

// usage
$postModel->getX($pictureModel);

这里发生的情况是,在模型中的这个单个实例中,getXPost传入另一个类的名称,并在该实例上执行“getX”方法(如果它存在并且是可调用的)。

您可以通过其他方式扩展它。例如,当方法无论如何都可以做到时,也许您不想传递实例:

public function getX($model_name = null) {
  if ($model_name && $class_exists($model_name) && is_callable(array($model_name, 'getX')) {
    $model = new $model_name;
    return $model->getX();

  } else {
    return self::DEFAULT_X;
  }
} 

// usage
$postModel->getX('Picture');

在这种情况下,您将模型作为字符串传入,然后该方法将完成其余的工作。虽然这可以更快地获得您想要的东西,但您可能会发现您不想一直使用新实例(或者您不能),因此需要对这种“方便”进行权衡方法。

但是,这仍然不能完全解决您的问题,因为您仍然必须一遍又一遍地为每个吸气剂重复此操作。相反,您可以尝试这样的事情:

public function __call($method, $args) {
  $class = $args[0];

  if (class_exists($class) && is_callable(array($class, $method))) {
    $model = new $class;  
    return $model->$method();
  }
}

// usage
$postModel->getX('Picture');
$postModel->getY('Quote');
$postModel->getZ('Picture');

如果您调用模型上不存在的函数,则将Post调用该魔术方法,并且它将启动您作为参数提供的模型名称的新实例,并getWhatever在其上调用该方法(如果存在) .

重要的是要注意,您不能在 中定义这些 getterPost,除非您想覆盖其他类中的方法。

但是,始终存在创建新实例的问题,为了解决这个问题,您可以使用一些依赖注入。这意味着您让Post该类包含它希望将来使用的其他类实例的列表,因此您可以随意添加和删除它们。

这就是我认为的实际解决方案,其他示例有望展示我是如何到达这里的(当然,将编辑以澄清事情)。

public $models = array();

public function addModel($instance) {
  $this->models[get_class($instance)] = $instance;
}

public function __call($method, $args) {
  $class = $args[0];

  if (array_key_exists($class, $this->models)) {
    $model = $this->models[$class];
    if (is_callable(array($model, $method)) {
      return $model->$method();
    }
  }     
}

// usage
$this->addModel($pictureModel);
$this->addModel($quoteModel);

$this->getX('Picture');
$this->getY('Quote');

在这里,您将现有的模型实例传递给Post类,然后将它们存储在一个数组中,并以类的名称为键。然后,当您使用上一个示例中描述的类时,它不会创建新实例,而是使用它已经存储的实例。这样做的好处是您可以对您的实例做一些您希望在Post模型中反映出来的事情。

这意味着您可以添加任意数量的需要插入的新模型,而您Post唯一需要做的就是注入它们addModel,并在这些模型上实现 getter。

它们都要求你告诉班级在某个时候要调用什么模型。既然您有一系列依赖模型,为什么不添加一种获取所有内容的方法呢?

public function __call($method, $args) {
  $class = $args[0];

  if (array_key_exists($class, $this->models)) {
    $model = $this->models[$class];
    if (is_callable(array($model, $method)) {
      return $model->$method();
    }
  } elseif ($class === 'all') {
    // return an array containing the results of each method call on each model
    return array_map(function($model) use ($method) {
      if (is_callable(array($model, $method) return $model->$method();
    }, $this->models);

  }    
}


// usage
$postModel->getX('all');

使用它,您将获得一个数组,其中包含getX您添加的每个模型上每个方法的返回值addModel。您可以创建非常强大的函数和类来完成所有这些工作,而无需重复繁琐的逻辑。

我不得不提一下,这些例子是未经测试的,但至少我希望你能做什么的概念已经清楚了。

注意: 同样的事情也可以应用于__GET__SET方法,用于访问属性。还值得一提的是,库已经使用这些魔术方法可能存在轻微风险,在这种情况下,您需要使代码更加智能。

于 2012-04-26T23:48:47.347 回答