【问题标题】:providing factory methods to other classes为其他类提供工厂方法
【发布时间】: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);

我的问题实际上是为我的对象类提供通用功能。我想要实现的是

  1. 代码重复最少,因此像fromArray 函数这样的通用代码应该在某个地方出现一次,并且可以在所有不同的对象类中重用。
  2. 适当的类型提示以支持我的 IDE 并在运行时强制兼容。
  3. 误用可能性极小,因此不应实例化不完整的代码片段。

我尝试了以下设计(所有这些都支持目标 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


    【解决方案1】:

    使用 trait 或抽象类,您可以实例化 static 而不是 self

    性状

    trait builder {
        public static function buildToo() {
            return new static();
        }
    }
    
    class Baz {
        use builder;
    }
    
    $b = Baz::buildToo();
    var_dump($b);
    
    /* output:
    object(Baz)#2 (0) {}
    */
    

    抽象类

    abstract class Foo {
        
        public static function build() {
            return new static();
        }
    }
    
    class Bar extends Foo {}
    
    $a = Bar::build();
    
    /* output:
    object(Bar)#1 (0) {}
    */
    

    这些是late static binding 的形式。

    从 PHP 8 开始,将于本月晚些时候(2020 年 11 月)发布,您甚至可以to declare static as a return type hint

    abstract class Foo {
        public static function build(): static
        {
            return new static();
        }
    }
    

    【讨论】:

    • 哇,静态作为返回类型提示可以完全解决这个问题!我还可以在 PHP7 中使用类型提示还是您的示例依赖于没有任何类型提示?
    • 对于 PHP new static()(而不是您的问题中的 new self()),并且您将无法使用类型提示(或使用 @ 987654331@,但这同样令人难过)。
    • 我刚刚试了一下,其实我可以在抽象类中输入提示self作为返回类型,即使是return new static,大概是因为子类的任何实例也被认为是实例的抽象类。这意味着我可以使用这个组合来做我想做的事!
    猜你喜欢
    • 2021-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-04
    • 1970-01-01
    相关资源
    最近更新 更多