iT邦幫忙

2021 iThome 鐵人賽

DAY 19
0
自我挑戰組

後端新手PHP+Laravel筆記系列 第 19

[Day19]PHP Interface介面

Interface對象接口也稱介面

使用介面(interface),可以指定某個類必須實現哪些方法,但不需要定義這些方法的具體內容。由於介面(interface)和類(class)、trait 共享了命名空間,所以它們不能重名。介面就像定義一個標準的類一樣,通過 interface 關鍵字替換掉 class 關鍵字來定義,但其中所有的方法都是空的。介面中定義的所有方法都必須是公有,這是介面的特性。在實踐中,往往出於兩個輔助目的使用介面: 因為實現了同一個介面,所以開發者創建的對象雖然源自不同的類,但可能可以交換使用。常用於多個數據庫的服務訪問、多個支付網關、不同的緩存策略等。可能不需要任何代碼修改,就能切換不同的實現方式。能夠讓函數與方法接受一個符合介面的參數,而不需要關心對像如何做、如何實現。這些介面常常命名成類似 Iterable、Cacheable、Renderable, 以便於體現出功能的含義。

注意: 雖然沒有禁止,但是強烈建議不要在接口中使用 構造器。因為這樣在對象實現接口時,會大幅降低靈活性。此外,也不能強制確保構造器遵守繼承規則,將導致不可預料的行為結果。

實現implements

要使用一個介面,使用 implements 操作符。類中必須使用介面中定義的所有方法,否則會報一個致命錯誤。類可以實現多個接口,用逗號來分隔多個接口的名稱。

警告:類使用(implement)兩個接口時,如果它們定義了相同名稱的方法,只有簽名相同的時候才是允許的。

警告:使用介面的時候,class 中的參數名稱不必和介面完全一致。然而, PHP 8.0 起語法開始支持命名參數, 也就是說調用方會依賴介面中參數的名稱。因此,強烈建議開發者的參數的命名,在類和介面中保持一致。

注意:接口也可以通過 extends 操作符繼承

注意:類實現介面時,必須以兼容的簽名定義介面中所有方法。

1. 介面範例
<?php

// 聲明一個'Template'介面
interface Template
{
    public function setVariable($name, $var);
    public function getHtml($template);
}


// 使用介面
// 正確寫法如下
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;
    }
}

// 下面的寫法是錯誤的,會報錯,因為沒有使用到該介面方法 getHtml():
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (Template::getHtml)
class BadTemplate implements Template
{
    private $vars = [];
  
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
}
?>
2. 可擴充介面
<?php
interface A
{
    public function foo();
}

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

// 正確寫法
class C implements B
{
    public function foo()
    {
    }

    public function baz(Baz $baz)
    {
    }
}

// 錯誤寫法導致致命錯誤
class D implements B
{
    public function foo()
    {
    }

    public function baz(Foo $foo) // 參考錯誤
    {
    }
}
?>
3. 拓展多介面
<?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()
    {
    }
}
?>
4. 使用介面常量
<?php
interface A
{
    const B = 'Interface constant';
}

// 输出接口常量
echo A::B;

// 錯誤寫法,因為常量不能被覆蓋。接口常量的概念和類常量是一樣的。
class B implements A
{
    const B = 'Class constant';
}
?>
5. 抽象(abstract)類的介面使用
<?php
interface A
{
    public function foo(string $s): string;

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

//  抽象類可能僅實現了接口的一部分。 
// 擴展該抽象類時必須實現剩餘部分
abstract class B implements A
{
    pubic function foo(string $s): string
    {
        return $s . PHP_EOL;
    }
}

class C extends B
{
    public function bar(int $i): int
    {
        return $i * 2;
    }
}
?>
6. 同時使用繼承和介面
<?php

class One
{
    /* ... */
}

interface Usable
{
    /* ... */
}

interface Updatable
{
    /* ... */
}

// 關鍵字順序至關重要: 'extends' 必須在前面
class Two extends One implements Usable, Updatable
{
    /* ... */
}
?>

資料來源: https://www.php.net/


上一篇
[Day18] 抽象類別
下一篇
[Day20] Laravel起步走
系列文
後端新手PHP+Laravel筆記30

尚未有邦友留言

立即登入留言