首頁  >  問答  >  主體

PHP 介面實際上如何改變類別的行為

<p>根據 PHP 文檔,</p> <blockquote> <p>物件介面可讓您建立指定類別必須實作哪些方法的程式碼,而無需定義這些方法的實作方式。 </p> </blockquote> <p>因此,介面就像一個具有預先定義方法的類,仍然需要使用 <code>-></code> 符號來存取</p> <p>但是,ArrayAccess 介面提供對物件作為陣列的存取。可以使用 <code>$object->property</code> 和 <code>$object["property"]</code></p> 存取對象 <p>我無法理解 ArrayAccess 如何讓更改物件語法成為可能。我寫了一段程式碼來嘗試複製 <code>ArrayAccess</code> 方法<em>只有一個</em>的效果,但它拋出錯誤</p> <pre class="brush:php;toolbar:false;">// Using the PHP ArrayAccess Interface namespace A { class myclass implements \ArrayAccess { public function offsetExists($offset) { return true; } public function offsetGet($offset) { // changed behaviour return $this->{$offset} ?? null; } public function offsetSet($offset, $value) {} public function offsetUnset($offset) {} } $myclass = new myclass(); $myclass->access = 'Interface'; echo $myclass['access']; // "Interface" }; //Trying to implement my own ArrayAccess Interface namespace B { interface MyArrayAccess { public function offsetGet($offset); } class myclass implements MyArrayAccess { public function offsetGet($offset) { // change behaviour return $this->{$offset} ?? null; } } $myclass = new myclass(); $myclass->access = 'Interface'; echo $myclass['access']; // Fatal error: Uncaught Error: Cannot use object of type B\myclass as array } </pre> <p>請幫我正確解釋。謝謝</p>
P粉289775043P粉289775043382 天前528

全部回覆(1)我來回復

  • P粉702946921

    P粉7029469212023-09-03 20:17:24

    我並不是說介面“改變了類別的行為”,而是說介面使擴充類別功能變得容易。

    要理解接口,作為一個物件導向的程式設計概念,我們首先應該理解它要解決什麼問題。

    「Interface」旨在解決什麼問題?

    介面是一種契約。這是在 PHP 中實作 duck-typing 的方法。您需要從庫編寫者的角度思考,他希望向其他人公開功能。例如,

    class Greeter
    {
        public function greet($person)
        {
            echo "Hello, {$person->getName()}!\n";
        }
    }
    

    為了確保函式庫使用者知道$person 需要有getName() 方法,您可以建立一個類別Person > 有一個getName() 方法。然後使用 類型宣告 來偵測程式碼執行時的潛在錯誤已解析。

    class Greeter
    {
        public function greet(Person $person)
        {
            echo "Hello, {$person->getName()}!\n";
        }
    }
    
    class Person
    {
        public string $name;
        public function getName(): string
        {
            return $this->name;
        }
    }
    

    假設有另一個庫可以用食物來餵養東西:

    class Feeder {
        public function feed(Eater $eater, string $food) {
            $eater->eat($food);
        }
    }
    
    class Animal {
        private $stomach = [];
        public function eat(string $food) {
            $stomach = $food;
        }
    }
    
    

    考慮這個...

    現在,假設使用者想要寫一個既可以吃飯又可以打招呼的 Pet 類別。使用者不想只是為了 Pet 再次編寫這些功能。

    如何寫 Pet 以便同時使用 GreeterFeeder 函式庫?

    也許是這樣的?

    class Pet extends Person, Animal {
    }
    

    不幸的是,PHP 不支援多重繼承。一個類別只能擴充一個類別。上面的程式碼無效。所以在目前的情況下,使用者只能使用其中一個函式庫。

    此外,對於不同的事物,「名稱」可能是一個非常不同的概念(例如,一個人可能會使用getName() 返回$first_name$last_name )。您的函式庫類別中可能沒有合理的預設實作 getName() 方法。

    所以,身為一個函式庫編寫者,您希望他/她的函式庫對使用者盡可能靈活。你能做什麼?

    如何用 PHP 中的「介面」解決這個問題?

    介面是方法簽章的宣告。這是在沒有特定類別/繼承要求的情況下聲明庫要求的捷徑。

    使用接口,您可以像這樣重寫兩個函式庫:

    Greeter 函式庫

    class Greeter {
        public function greet(Namer $namer) {
            echo "Hello, {$namer->getName()}!\n";
        }
    }
    
    interface Namer {
        public function getName(): string;
    }
    

    Feeder

    class Feeder {
        public function feed(Eater $eater, string $food) {
            $eater->eat($food);
        }
    }
    
    interface Eater {
        public function eat(string $food);
    }
    
    

    不需要一個特定的類別(或父類別繼承),一個類別可以實作多個介面。所以下面的 Pet 類別在 PHP 中是完全有效的:

    class Pet implements Namer, Eater {
        private array $stomach = [];
        private string $name = '';
    
        public function __construct(string $name)
        {
            $this->name = $name;
        }
    
        /**
         * Implements Namer.
         */
        public function getName(): string
        {
            return $this->name;
        }
    
        /**
         * Implements Eater.
         */
        public function eat(string $food)
        {
            $this->stomach[] = $food;
        }
    }
    
    $greeter = new Greeter();
    $feeder = new Feeder();
    $pet = new Pet('Paul');
    
    $greeter->greet($pet);
    $feeder->feed($pet, 'a biscuit');
    

    現在,此 Pet 類別的物件可以與 Greeter 函式庫和 Feeder 函式庫一起使用。

    ArrayAccess 介面怎麼樣?

    ArrayAccess 介面不是由第三方宣告的介面庫編寫者,而是由核心 PHP 編寫者編寫。核心PHP編寫器對此提供了更深刻的支援。

    有點像是我們之前提到的接口,PHP 為實作它的類別提供功能。但核心 PHP 不是提供上面的 GreeterFeeder 範例,而是提供 實作 ArrayAccess 的類別的語法糖。這表示您可以使用更簡潔的程式碼來處理實作 AccessAccess 介面的類別。

    在官方範例中,

    <?php
    class Obj implements ArrayAccess {
        private $container = array();
    
        public function __construct() {
            $this->container = array(
                "one"   => 1,
                "two"   => 2,
                "three" => 3,
            );
        }
    
        public function offsetSet($offset, $value) {
            if (is_null($offset)) {
                $this->container[] = $value;
            } else {
                $this->container[$offset] = $value;
            }
        }
    
        public function offsetExists($offset) {
            return isset($this->container[$offset]);
        }
    
        public function offsetUnset($offset) {
            unset($this->container[$offset]);
        }
    
        public function offsetGet($offset) {
            return isset($this->container[$offset]) ? $this->container[$offset] : null;
        }
    }
    

    如果您實現了它們,則代替這些:

    $obj = new Obj;
    $obj->offsetSet(10, "hello");
    $obj->offsetSet(11, "world");
    if ($obj->offsetUnset(12)) {
        $obj->offsetUnset(12);
    }
    echo $obj->offsetGet(11);
    

    您可以將 $obj 與類似陣列的語法一起使用,以使程式碼更短:

    $obj = new Obj;
    
    $obj[10] = "hello";
    $obj[11] = "world";
    if (isset($obj[12])) {
        unset($obj[12]);
    }
    echo $obj[11];
    

    回覆
    0
  • 取消回覆