Objektsgränssnitt

Från Wiki.linux.se
Hoppa till navigering Hoppa till sök

Objektgränssnitt (Interfaces)

Objektgränssnitt (interfaces) gör det möjligt att skapa kod som specificerar vilka metoder en klass måste implementera, utan att behöva definiera hur dessa metoder ska implementeras. Gränssnitt delar namnrymden med klasser och traits, så de kan inte använda samma namn.

Gränssnitt definieras på samma sätt som en klass, men med nyckelordet interface istället för class och utan att några av metoderna har något innehåll definierat.

Alla metoder som deklareras i ett gränssnitt måste vara publika; detta är själva grunden för ett gränssnitt.

I praktiken tjänar gränssnitt två kompletterande syften:

  • För att låta utvecklare skapa objekt av olika klasser som kan användas omväxlande eftersom de implementerar samma gränssnitt eller gränssnitt. Ett vanligt exempel är olika databasåtkomsttjänster, olika betalningsgateways eller olika cachingstrategier. Olika implementationer kan bytas ut utan att kräva några ändringar i koden som använder dem.
  • För att tillåta en funktion eller metod att acceptera och arbeta med en parameter som följer ett gränssnitt, utan att bry sig om vad objektet annars kan göra eller hur det är implementerat. Dessa gränssnitt är ofta namngivna som Iterable, Cacheable, Renderable, eller liknande för att beskriva betydelsen av beteendet.

Gränssnitt kan definiera magiska metoder för att kräva att implementerande klasser implementerar dessa metoder.

Notera: Även om de stöds, är det starkt avrått att inkludera konstruktorer i gränssnitt. Att göra det minskar avsevärt flexibiliteten för objektet som implementerar gränssnittet. Dessutom är konstruktörer inte påtvingade av arvreglerna, vilket kan orsaka inkonsekvent och oväntat beteende.

implements

För att implementera ett gränssnitt används operatorn implements. Alla metoder i gränssnittet måste implementeras inom en klass; att inte göra det kommer att resultera i ett fatalt fel. Klasser kan implementera mer än ett gränssnitt om så önskas genom att separera varje gränssnitt med ett komma.

Varning: En klass som implementerar ett gränssnitt kan använda ett annat namn för sina parametrar än gränssnittet. Men från och med PHP 8.0 stöder språket namngivna argument, vilket innebär att anropare kan förlita sig på parameternamnet i gränssnittet. Av den anledningen rekommenderas det starkt att utvecklare använder samma parameternamn som i det implementerade gränssnittet.

Notera: Gränssnitt kan utökas som klasser med hjälp av operatorn extends.

Notera: Klassen som implementerar gränssnittet måste deklarera alla metoder i gränssnittet med en kompatibel signatur. En klass kan implementera flera gränssnitt som deklarerar en metod med samma namn. I detta fall måste implementeringen följa signaturkompatibilitetsreglerna för alla gränssnitt. Så kovarians och kontravarians kan tillämpas.

Konstanter

Det är möjligt för gränssnitt att ha konstanter. Gränssnittskonstanter fungerar exakt som klasskonstanter. Före PHP 8.1.0 kan de inte åsidosättas av en klass/gränssnitt som ärver dem.

Exempel

Exempel #1 Gränssnittsexempel

<?php

// Deklarera gränssnittet 'Template'
interface Template
{
    public function setVariable($name, $var);
    public function getHtml($template);
}

// Implementera gränssnittet
// Detta fungerar
class WorkingTemplate implements Template
{
    private $vars = [];
  
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
  
    public function getHtml($template)
    {
        foreach($this->vars as $name => $value) {
            $template = str_replace('{' . $name . '}', $value, $template);
        }
 
        return $template;
    }
}

// Detta kommer inte att fungera
// Fatalt fel: Klassa BadTemplate innehåller 1 abstrakta metoder
// och måste därför deklareras som abstrakt (Template::getHtml)
class BadTemplate implements Template
{
    private $vars = [];
  
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
}
?>

Exempel #2 Utbyggbara gränssnitt

<?php
interface A
{
    public function foo();
}

interface B extends A
{
    public function baz(Baz $baz);
}

// Detta fungerar
class C implements B
{
    public function foo()
    {
    }

    public function baz(Baz $baz)
    {
    }
}

// Detta fungerar inte och resulterar i ett fatalt fel
class D implements B
{
    public function foo()
    {
    }

    public function baz(Foo $foo)
    {
    }
}
?>

Exempel #3 Varianskompatibilitet med flera gränssnitt

<?php
class Foo {}
class Bar extends Foo {}

interface A {
    public function myfunc(Foo $arg): Foo;
}

interface B {
    public function myfunc(Bar $arg): Bar;
}

class MyClass implements A, B
{
    public function myfunc(Foo $arg): Bar
    {
        return new Bar();
    }
}
?>

Exempel #4 Flera gränssnittsarv

<?php
interface A
{
    public function foo();
}

interface B
{
    public function bar();
}

interface C extends A, B
{
    public function baz();
}

class D implements C
{
    public function foo()
    {
    }

    public function bar()
    {
    }

    public function baz()
    {
    }
}
?>

Exempel #5 Gränssnitt med konstanter

<?php
interface A
{
    const B = 'Gränssnittskonstant';
}

// Skriver ut: Gränssnittskonstant
echo A::B;


class B implements A
{
    const B = 'Klasskonstant';
}

// Skriver ut: Klasskonstant
// Före PHP 8.1.0 fungerade detta dock inte eftersom det inte var
// tillåtet att åsidosätta konstanter.
echo B::B;
?>

Exempel #6 Gränssnitt med abstrakta klasser

<?php
interface A
{
    public function foo(string $s): string;

    public function bar(int $i): int;
}

// En abstrakt klass kan implementera endast en del av ett gränssnitt.
// Klasser som utökar den abstrakta klassen måste implementera resten.
abstract class B implements A
{
    public function foo(string $s): string
    {
        return $s . PHP_EOL;
    }
}

class C extends B
{
    public function bar(int $i): int
    {
        return $i * 2;
    }
}
?>

Exempel #7 Utöka och implementera samtidigt

<?php

class One
{
    /* ... */
}

interface Usable
{
    /* ... */
}

interface Updatable
{
    /* ... */
}

// Nyckelordordningen här är viktig. 'extends' måste komma först.
class Two extends One implements Usable, Updatable
{
    /* ... */
}
?>

Sidslut

Orginalhemsidan på Engelska : https://www.php.net/manual/en/language.oop5.interfaces.php
PHP
Språkreferens
Språkreferens#Klasser_och_Objekt