【发布时间】:2021-02-17 02:40:03
【问题描述】:
我想创建一个GenericCollection 类,该类使用另一个类作为模板(“对象”)进行实例化,然后对其执行任务,例如从数据库加载对象。我的收藏基本上是这样的:
final class GenericCollection {
private $template;
private $db;
public function __construct(CollectableInterface $template, DbInterface $db) {
$this->template = $template;
$this->db = $db;
}
public function load(array $filter): iterable {
$results = $this->db->read(['t' => $this->template::tableName()], $filter, $this->template::getFields());
foreach ($results as $row) {
yield $this->template::fromArray($row);
}
}
}
$collection = new GenericCollection(MyObject::template(), $db);
我的问题实际上是为我的对象类提供通用功能。我想要实现的是
- 代码重复最少,因此像
fromArray函数这样的通用代码应该在某个地方出现一次,并且可以在所有不同的对象类中重用。 - 适当的类型提示以支持我的 IDE 并在运行时强制兼容。
- 误用可能性极小,因此不应实例化不完整的代码片段。
我尝试了以下设计(所有这些都支持目标 1):
1。接口+特质
interface CollectableInterface {
public static function template(): object;
public static function getFields(): array;
public static function tableName(): string;
public static function fromArray(array $data): object;
}
trait CollectableTrait {
public static function fromArray(array $data = []): object {
foreach (get_object_vars($obj = new self) as $property => $default) {
$obj->$property = $data[$property] ?? $default;
}
return $obj;
}
public static function template(): object {
return new self;
}
}
final class MyObject implements CollectableInterface {
use CollectableTrait;
// properties, implementations of tableName() and getFields()
}
这不起作用,因为类型提示不兼容。我知道,实际上没有办法让它们适合。使用object 似乎是使接口和特征兼容的唯一方法(self 失败,因为特征没有实现接口,如果我显式键入提示接口,同样的问题),但是我不能实际上类型提示集合的构造函数中的接口(如果我使用 trait 作为类型提示,同样的问题,假设它甚至会运行)。
我想出的使这种方法起作用的唯一方法是删除几乎所有的类型提示,这违反了我的第二个目标。
2。抽象类
abstract class CollectableObject {
public static function fromArray(array $data = []): self {
foreach (get_object_vars($obj = new self) as $property => $default) {
$obj->$property = $data[$property] ?? $default;
}
return $obj;
}
public static function template(): self {
return new self;
}
abstract public static function getFields(): array;
abstract public static function tableName(): string;
}
final class MyObject extends CollectableObject {
// properties, implementations of tableName() and getFields()
}
当然还要将 Collection 构造函数中的类型提示更改为CollectableObject。
这不起作用,因为我无法在常用方法中实例化抽象类 (new self)。
3。要扩展的非抽象类
这与上一个基本相同,但类不是抽象的,因此常用方法可以创建一个实例。这违反了目标 3,因为此类不应该是可实例化的,但即使我将构造函数设为私有以停止 new CollectableObject,也会通过调用 CollectableObject::fromArray()。
有什么方法可以在此设置中实现我的所有目标?我的整个前提/整体结构有缺陷吗?是否有不同的方法或某种方式来修改我的方法以使其工作?
【问题讨论】:
标签: php oop interface abstract-class traits