简体   繁体   中英

Can we force concrete classes to implement an interface specified in an abstract class in PHP?

Is it possible to specify an interface in an abstract class that must be implemented by concrete implementations in PHP?

At the moment we are doing something like the following (hopefully made simple enough to follow):

<?php

declare(strict_types=1);

interface SensorObserver
{
    public function sensorChanged( array $sensorData );
}

class SensorSubject
{
    protected $observers = [];

    public function addSensorObserver( SensorObserver $sensorObserver ) : bool
    {
        if ( !in_array( $sensorObserver, $this->observers ) )
        {
            $this->observers[] = $sensorObserver;
            return true;
        }
        return false;
    }

    public function removeSensorObserver( SensorObserver $sensorObserver ) : bool
    {
        if ( !in_array( $sensorObserver, $this->observers ) )
        {
            return false;
        }
        else
        {
            unset( $this->observers[array_search( $sensorObserver, $this->observers )]); 
            return true;
        }
    }

    public function notifyObservers( array $sensorData )
    {
        foreach( $this->observers as $observer )
        {
            $observer->sensorChanged( $sensorData );
        }
    }
}

abstract class GenericDisplay
{
    protected $name;
    protected $sensorData;
    protected $displayData;

    abstract public function showDisplay();
    abstract protected function generateDisplayData();

    public function __construct( string $name )
    {
        $this->name = $name;
        $this->sensorData = [];
        $this->displayData = [];
    }
}

class DisplayDeviceA extends GenericDisplay implements SensorObserver
{
    public function __construct()
    {
        parent::__construct( 'DisplayDeviceA' );
        $this->sensorData = [ 'SensorTypeA' => 0.0, 'SensorTypeB' => 0.0 ];
        $this->generateDisplayData();
    }

    public function showDisplay()
    {
        echo PHP_EOL . "{$this->name}: " . PHP_EOL;
        foreach ( $this->displayData as $key => $value )
        {
            echo "{$key}: {$value}" . PHP_EOL;
        }
    }

    protected function generateDisplayData()
    {
        // Complicated processing done for output here :)
        $this->displayData = $this->sensorData;
    }

    public function sensorChanged( array $sensorData )
    {
        $dirtySensorData = false;

        foreach( $sensorData as $key => $value )
        {
            if ( array_key_exists( $key, $this->sensorData ) )
            {
                // This is just an example, <imagine custom processing here  when sensor data changes />,
                // otherwise we could just implement in the base abstract class or perhaps with a trait
                $this->sensorData[$key] = $value;
                $dirtySensorData = true;
            }
        }

        if ( $dirtySensorData )
        {
            $this->generateDisplayData();
        }
    }
}

class DisplayDeviceB extends GenericDisplay implements SensorObserver
{
    public function __construct()
    {
        parent::__construct( 'DisplayDeviceB' );
        $this->sensorData = [ 'SensorTypeA' => 0.0, 'SensorTypeB' => 0.0, 'SensorTypeC' => 0.0 ];
        $this->generateDisplayData();
    }

    public function showDisplay()
    {
        echo PHP_EOL . "{$this->name}: " . PHP_EOL;
        foreach ( $this->displayData as $key => $value )
        {
            echo "{$key} => {$value}" . PHP_EOL;
        }
    }

    protected function generateDisplayData()
    {
        // Complicated processing done for output here :)
        $this->displayData = $this->sensorData;
    }

    public function sensorChanged( array $sensorData )
    {
        $dirtySensorData = false;

        foreach( $sensorData as $key => $value )
        {
            if ( array_key_exists( $key, $this->sensorData ) )
            {
                // Again, just an example...
                $this->sensorData[$key] = $value;
                $dirtySensorData = true;
            }
        }

        if ( $dirtySensorData )
        {
            $this->generateDisplayData();
        }
    }
}

class DisplayDeviceC extends GenericDisplay implements SensorObserver
{
    public function __construct()
    {
        parent::__construct( 'DisplayDeviceC' );
        $this->sensorData = [ 'SensorTypeB' => 0.0, 'SensorTypeD' => 0.0 ];
        $this->generateDisplayData();
    }

    public function showDisplay()
    {
        echo PHP_EOL . "{$this->name}: " . PHP_EOL;
        foreach ( $this->displayData as $key => $value )
        {
            echo "{$key} --> {$value}" . PHP_EOL;
        }
    }

    protected function generateDisplayData()
    {
        // Complicated processing done for output here :)
        $this->displayData = $this->sensorData;
    }

    public function sensorChanged( array $sensorData )
    {
        $dirtySensorData = false;

        foreach( $sensorData as $key => $value )
        {
            if ( array_key_exists( $key, $this->sensorData ) )
            {
                // Again, just an example...
                $this->sensorData[$key] = $value;
                $dirtySensorData = true;
            }
        }

        if ( $dirtySensorData )
        {
            $this->generateDisplayData();
        }
    }
}

$testDisplays = [ new DisplayDeviceA(), new DisplayDeviceB(), new DisplayDeviceC() ];
foreach( $testDisplays as $display )
{
    $display->showDisplay();
}

$sensorSubject = new SensorSubject();
foreach( $testDisplays as $display )
{
    $sensorSubject->addSensorObserver( $display );
}

$sensorSubject->notifyObservers( ['SensorTypeB' => 10.0, 'SensorTypeD' => 5.0] );

foreach( $testDisplays as $display )
{
    $display->showDisplay();
}

Wondering if there is essentially some way to write:

interface SensorObserver
{
    public function sensorChanged( array $sensorData );
}

abstract class GenericDisplay implements SensorObserver
{
    ...
    abstract public function sensorChanged( array $sensorData );
    ...
}

class ConcreteDisplay extends GenericDisplay
{
    public function sensorChanged( array $sensorData ) { ... };
}

Thanks to @Mark Baker and @Pael Petrov below. If anybody stumbles across this, a working example:

<?php

interface AnInterface
{
    public function foo();
}

abstract class AbstractClass implements AnInterface
{
    //...
}

class ConcreteClass extends AbstractClass
{
    public function foo()
    {
        echo 'foo';
    }
}


$concrete = new ConcreteClass();
$concrete->foo();

If an abstract class implements some interface concrete classes automatically implement all the interfaces of the abstract class so you just need to extend the abstract class:

class ConcreteDisplay extends GenericDisplay
{
    //implement interface methods here 
    //if not implemented in the parend class
}

This will do the job

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM