Php 编程范例:带继承的强类型参数
警告:可能导致TL:DR 我使用的是PHP5.3.10,有以下问题。我确实有一个抽象类Php 编程范例:带继承的强类型参数,php,oop,design-patterns,architecture,Php,Oop,Design Patterns,Architecture,警告:可能导致TL:DR 我使用的是PHP5.3.10,有以下问题。我确实有一个抽象类DataMapper,它是为我想要持久化的特定DataModel而扩展的。以下代码执行此操作: abstract class DataMapper { public abstract function findById($id); public abstract function fetchAll(); public abstract function save(IModel $mode
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,它确实需要一组特定的参数和方法。但是,一些模型确实有相应的数据映射器所需的额外方法-在本例中,是一个checkPassword()
方法,用于根据存储的哈希值测试密码。此方法还可以指示数据映射器重新刷新刚刚测试的密码,并根据新的要求(例如,密码哈希函数的难度增加)进行更新
因此,我实际想要的是将PersonMapper的签名更改为PersonMapper::save(PersonModel$model)
——例如,在另一个数据映射器中更改为PostMapper::save(PostModel$model)
,等等。这是因为这些数据映射器需要特定的签名。因此,我的理想解决方案如下所示:
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... */ }
注意抽象类中的更新保存方法及其实现。由于PersonModel
是从Model
继承而来的,因此显然有一组共同的基本签名,因此我希望它可以正常工作。但事实并非如此——PHP抱怨childclass PersonMapper中的接口发生了更改
我的问题:
- 是否有另一种使用PHP5.3.10的解决方案可以更好地表达这种关系
- 它是否在更高版本的PHP中工作,因此可能值得升级服务器
interface OtherModel {
public function getThis();
}
interface OtherOtherModel {
public function getThat();
}
您的模型类可能实现一个或多个接口
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() {
// ...
}
}
您的具体映射器类可以使用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!');
}
}
我可以想出另一种可能的方法,通过使用接口来定义实现。
例如:
interface PersonModelAware {
public function save(PersonModel $model);
}
interface OtherModelAware {
public function save(OtherModel $model);
}
等等。您的抽象方法可能有默认的保存方法,或者根本没有保存方法。继承类将实现它需要的接口
总而言之,使您的类型更具体将无法工作,因为抽象方法清楚地表明它需要一个模型 您可以尝试改用接口
interface OtherModel {
public function getThis();
}
interface OtherOtherModel {
public function getThat();
}
您的模型类可能实现一个或多个接口
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() {
// ...
}
}
您的具体映射器类可以使用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!');
}
}
我可以想出另一种可能的方法,通过使用接口来定义实现。
例如:
interface PersonModelAware {
public function save(PersonModel $model);
}
interface OtherModelAware {
public function save(OtherModel $model);
}
等等。您的抽象方法可能有默认的保存方法,或者根本没有保存方法。继承类将实现它需要的接口
总而言之,使您的类型更具体将无法工作,因为抽象方法清楚地表明它需要一个模型 是的,但我现在的解决方案也可以做到这一点。问题是,是否有可能在方法的签名中而不是在主体中获得需求。如果您只阅读签名,您仍然不知道save函数是否需要PersonModel而不是其他模型,而这正是我试图实现的。@Lars好的。您的方法的问题是您使提示类型更显式,这将不起作用。我可以想出另一种方法,而不是在抽象类中声明抽象save方法,而是创建不同的接口,这些接口包含save(PersonModel$model)等。这是一种有效的方法。我对DataMapper做了一些修改,使其不包含抽象方法,现在实现了默认行为。我不完全理解你的评论的解决方案-你能用一个例子更新你的答案吗?是的,但我现在的解决方案也可以。问题是,是否有可能在方法的签名中而不是在主体中获得需求。如果您只阅读签名,您仍然不知道save函数是否需要PersonModel而不是其他模型,而这正是我试图实现的。@Lars好的。您的方法的问题是您使提示类型更显式,这将不起作用。我可以想出另一种方法,而不是在抽象类中声明抽象save方法,而是创建不同的接口,这些接口包含save(PersonModel$model)等。这是一种有效的方法。我对DataMapper做了一些修改,使其不包含抽象方法,现在实现了默认行为。我不完全理解你的意见的解决方案-你能用一个例子更新你的答案吗?