有没有什么好的方法既能遵守 covariance/contravariance 规则又不写重复代码?

Is there a good way to adhere to the covariance/contravariance rule while not writing repetitive code?

我有一些代码如下。我尽量将代码简化为易于理解。

class BaseFooClass {
    protected $keys = [];
    private $map = [];
    public function __construct($keyValuePairs) {
        foreach($this->keys as $key => $value) {
            $this->map[$key] = $keyValuePairs[$key] ?? null;
        }
    }
}

class ChildFooClass1 extends BaseFooClass {
    protected $keys = ['foo1_a', 'foo1_b'];
}

class ChildFooClass2 extends BaseFooClass {
    protected $keys = ['foo2_a', 'foo2_b', 'foo2_c'];
}

//... (there are like a hundred child foo classes)

abstract class BaseBarClass {
    protected $classIndex;
    protected function getFooBase(int $dataIndex) : ?BaseFooClass 
    {
        // GetRemoteData is assumed to be a global function, the important thing here is the retrieved data depends on classIndex and dataIndex
        // If $classIndex is 1, the $keyValuePairs will look like ['foo1_a' => value1, 'foo1_b' => value2] where value1 and value2 depend on $dataIndex
        $keyValuePairs = GetRemoteData($this->classIndex, $dataIndex);
        if (checkDataIntegrity($keyValuePairs)) {
            $class = "ChildFooClass" . $this->classIndex;
            return new $class($keyValuePairs);
        }
        return null;
    }
}

class ChildBarClass1 extends BaseBarClass {
    protected $classIndex=1;
    public function getFoo(int $dataIndex) : ?ChildFooClass1 
    {
        // this line violates covariance/contravariance rule
        return $this->getFooBase($dataIndex);
    }
}

class ChildBarClass2 extends BaseBarClass {
    protected $classIndex=2;
    // input to getFoo in each BarClass can be different
    public function getFoo($someInput) : ?ChildFooClass2
    {
        $dataIndex = $this->calculateDataIndex($someInput);
        // this line also violates covariance/contravariance rule
        return $this->getFooBase($dataIndex);
    }
}

我想要满足的三个标准是:

(1) 我想确保 ChildBarClass1::getFoo 只有 returns ChildFooClass1,而且还要确保 BaseBarClass::getFooBase 只有 returns 继承 BaseFooClass 的 class .经验法则:使类型声明尽可能严格。

(2) 我要确认没有重复代码。我不想在每个 getFoo 函数中调用 GetRemoteData 和 checkDataIntegrity。其实在真正的代码中也有比这两个多得多的东西。

(3) 我想遵守 covariance/contravariance 规则。目前,代码中显示的倒数第三行违反了此规则,因为它使用了一个函数,该函数 returns 父 foo class.

我似乎想不出一个好的解决办法。我想出的任何东西要么违反规则,要么使代码看起来像“hack”而且非常丑陋。如果有人可以提供解决此问题的好方法,而不破坏三个标准中的任何一个或更改一般结构(即每个 Foo class 都有相应的 Bar class 处理创建 Foo 对象),我真的很感激。

根据您的代码,您可以简单地从 getFooBase() 中删除 return 类型(或者使用 php v8,使其成为 : mixed)。该方法不属于 public api。因此这里没有真正的损失。

由于您的 getFoo() 和 return 值是单独输入的,如果 getFooBase() return 不合适,您无论如何都会收到类型错误。