警告:可能导致TL:DR
我正在使用PHP 5.3.10并遇到以下问题。我有一个抽象类DataMapper
,它针对我想要保留的特定DataModel
进行了扩展。以下代码可以解决这个问题:
abstract class DataMapper {
public abstract function findById($id);
public abstract function fetchAll();
public abstract function save(IModel $model); // DISCUSSION
/* more helper functions here */
}
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic ... */ }
public function fetchAll() { /* ...magic ... */ }
public function save(IModel $model) { /* ...magic ... */ } // DISCUSSION
}
interface IModel {
public function setOptions(array $options);
public function toArray();
}
abstract class Model implements IModel {
protected $_fields = array();
protected $_data = array();
public function setOptions(array $options) { /* ...magic ... */ }
public function toArray() { /* ...magic ... */ }
public function __construct(array $options = null) { /* ...magic ... */ }
public function __set($name, $value) { /* ...magic ... */ }
public function __get($name) { /* ...magic ... */ }
}
class PersonModel extends Model {
protected $_fields = array('id', 'name', 'passhash', /*...*/);
public function setId($value) {
/* ...Validation happening... */
$this->_data['id'] = $value;
return $this;
}
public function checkPassword($password) { /* ...magic... */ }
}
这很好,但对我的感觉真的很古怪。
正如您所看到的,我使用了一个接口IModel
来告诉DataMapper,它确实需要一组参数和方法。但是,某些模型确实具有相应DataMapper所需的额外方法 - 在该示例中,使用checkPassword()
方法,该方法用于针对存储的哈希值测试密码。此方法还可以指示DataMapper重新更新刚刚测试的密码并根据新要求更新密码(例如,密码哈希函数的难度增加)。
所以我真正想要的是将PersonMapper的签名更改为PersonMapper::save(PersonModel $model)
- 例如在另一个DataMapper中PostMapper::save(PostModel $model)
等等。这是由于这些DataMappers需要一定的签名。所以我的理想解决方案如下:
abstract class DataMapper {
public abstract function findById($id);
public abstract function fetchAll();
public abstract function save(Model $model); // UPDATED
}
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic... */ }
public function fetchAll() { /* ...magic... */ }
public function save(PersonModel $model) { /* ...magic... */ } // UPDATED
}
abstract class Model { /* ...unchanged... */ }
class PersonModel extends Model { /* ...unchanged... */ }
注意抽象类及其实现中的Update save-Methods。由于PersonModel
继承自Model
,因此显然有一个共同的基本签名集,我希望这可以正常工作。但事实并非如此 - PHP抱怨子类PersonMapper中的更改接口
我的问题:
答案 0 :(得分:1)
您可以尝试使用接口。
interface OtherModel {
public function getThis();
}
interface OtherOtherModel {
public function getThat();
}
您的Model Class可能会实现一个或多个接口......
class PersonModel extends Model implements OtherModel {
protected $_fields = array('id', 'name', 'passhash', /*...*/);
public function setId($value) {
/* ...Validation happening... */
$this->_data['id'] = $value;
return $this;
}
public function checkPassword($password) { /* ...magic... */ }
public function getThis() {
// ...
}
}
您的具体Mapper类可以使用instanceof来检查此模型是否完成了它应该做的事情。
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic... */ }
public function fetchAll() { /* ...magic... */ }
public function save(Model $model) {
// verify that certain methods are implemented...
// throw an exception or reacting accordingly
print ($model instanceof PersonModel)? 'yes' : 'no';
print ($model instanceof OtherOtherModel)? 'yes' : 'no';
}
}
另一种可能的方法可能如下:
<?php
abstract class DataMapper {
public abstract function findById($id);
public abstract function fetchAll();
public function save(Model $model) {
throw new Exception('You have to implement this!');
}
}
如果在继承类中未覆盖save方法,则抛出异常。 现在你可以真正使用不同的typehint。 这将有效:
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic... */ }
public function fetchAll() { /* ...magic... */ }
public function save(PersonModel $model) {
// do something
}
}
通过使用接口来定义实现,我可以想到另一种可能的方法。 例如:
interface PersonModelAware {
public function save(PersonModel $model);
}
interface OtherModelAware {
public function save(OtherModel $model);
}
等。您的抽象方法可能具有默认保存方法或根本没有保存方法。继承类将实现它所需的接口。
总结一下,让你的类型更具体是不行的,因为抽象方法清楚地表明它需要一个模型。