How to force an implementation of a protected stat

2019-05-10 13:24发布

问题:

I'm trying to write an abstract class (or interface) which forces the extending class to implement a protected static function. But this is neither possible with an abstract class nor an interface.

Errors:

  • static functions should not be abstract
  • access type for interface members must be omitted

Any ideas how to accomplish that?

UPDATE

The purpose is basically to call the public function statically. This way the class does not need to be instanciated. It is also not necessary to make _doSpecificStuff() callable from class-external code.

abstract class Foo
{
    public static function doStuff()
    {
        [generic code] 

        static::_doSpecificStuff();
    }

    // sth like that would be nice to have:
    abstract static protected function _doSpecificStuff();
}

回答1:

From a theoretical as well as a practical standpoint, there's no real need to declare a static method abstract.

Abstract methods are there to have a child class fill in a blank. That's typically because the parent class (the original abstract class) does some generic operation, but can/must be adapted to certain specific situations and can thus force child classes to implement only this particular variable part in the otherwise generic algorithm. Abstract methods are supposed to be a blank spot within a larger algorithm.

A parent method would call implementations of its abstract methods without knowing or caring who implements them or how they're implemented:

abstract class Foo {

    public function someAlgo() {
        $foo = $this->doSomethingSpecific();
        ...
    }

    abstract protected function doSomethingSpecific();

}

Foo doesn't care who or what fills in the blank doSomethingSpecific, it just relies on it being there and its signature, which abstract enforces. The specific object which implements this method or how it implements it is variable. This is important and is at the core of the issue.

Declaring a static method abstract is pretty useless in this scenario. Any static method can just as well be implemented as a non-static method, so there's no use for it here. If the class itself is supposed to call the abstract method as part of a larger generic algorithm as described above, there's no need for a static method.

So the only scenario left for a static method is for a public static method which is callable from anywhere:

abstract class Foo {

    abstract public static function bar();

}

class Baz extends Foo {

    public static function bar() { ... }

}

Baz::bar();

The thing is, since the abstract class Foo is not itself calling this function but this function is only called from external code, you're not really talking about a fill-in-the-blank method, you're talking about an interface. So, you should be using an interface instead.
But even there, since you have to type the specific class name in your source code, hardcoded, there's little point for an interface as well.

The point of declaring an interface or abstract method signature is that you want to fix the method signature so any code can call that particular method without caring what object it's calling it on in particular. But since you have to hardcode the class name, there's no variability in the object you're calling it on. If you type Baz::bar(), you know exactly what class you're calling what method on. Therefore there's little point in abstracting the interface/signature.


Compare:

interface FooInterface {

    public function bar();

}

function baz(FooInterface $foo) {
    $foo->bar();
}

The function baz can rely on its argument having a bar() method due to the interface declaration. The specific object that's implementing the method is irrelevant and will vary.

abstract class Foo {

    public function someAlgo() {
        $foo = $this->doSomethingSpecific();
        ...
    }

    abstract protected function doSomethingSpecific();

}

The class Foo can rely on it having the doSomethingSpecific method. The specific object that's implementing the method is irrelevant and will vary.

abstract class Foo {

    abstract public static function bar();

}

class Baz extends Foo {

    public static function bar() { ... }

}

Baz::bar();

What exactly are you relying on or abstracting here? You can be pretty darn sure Baz will have the method bar() every time, because you're only ever calling it on the same hardcoded class. Nothing is variable here.