Is there a way to redefine a type hint to a descendant class when extending an abstract class?

前端 未结 3 1704
名媛妹妹
名媛妹妹 2020-12-03 06:45

I will be using the following example to illustrate my question:

class Attribute {}

class SimpleAttribute extends Attribute {}



abstract class AbstractFac         


        
3条回答
  •  既然无缘
    2020-12-03 07:34

    I wouldn't expect so, as it can break type hinting contracts. Suppose a function foo took an AbstractFactory and was passed a SimpleFactory.

    function foo(AbstractFactory $maker) {
        $attr = new Attribute();
        $maker->update($attr, 42);
    }
    ...
    $packager=new SimpleFactory();
    foo($packager);
    

    foo calls update and passes an Attribute to the factory, which it should take because the AbstractFactory::update method signature promises it can take an Attribute. Bam! The SimpleFactory has an object of type it can't handle properly.

    class Attribute {}
    class SimpleAttribute extends Attribute {
        public function spin() {...}
    }
    class SimpleFactory extends AbstractFactory {
        public function update(SimpleAttribute $attr, $data) {
            $attr->spin(); // This will fail when called from foo()
        }
    }
    

    In contract terminology, descendent classes must honor the contracts of their ancestors, which means function parameters can get more basal/less specified/offer a weaker contract and return values can be more derived/more specified/offer a stronger contract. The principle is described for Eiffel (arguably the most popular design-by-contract language) in "An Eiffel Tutorial: Inheritance and Contracts". Weakening and strengthening of types are examples of contravariance and covariance, respectively.

    In more theoretical terms, this is an example of LSP violation. No, not that LSP; the Liskov Substitution Principle, which states that objects of a subtype can be substituted for objects of a supertype. SimpleFactory is a subtype of AbstractFactory, and foo takes an AbstractFactory. Thus, according to LSP, foo should take a SimpleFactory. Doing so causes a "Call to undefined method" fatal error, which means LSP has been violated.

提交回复
热议问题