PHP基礎教程十二之抽象、接口

本節講解的內容

  • 抽象
  • 接口
  • final的使用
  • 類常量

前言

在PHP中的面向對象是通過定義類,來完成對象的示例化,而PHP的抽象類和接口類可以說是類的一種規範,通過定義這兩種類來對類進行強制約束,雖然這兩種都是對類的約束,但本質上他們還是有區別的。

抽象類

抽象類的概念我們可以用動物的繼承關係來說明問題,當我們寫父類Animal類時,其中有兩個方法sleep(),eat(),因爲不知道具體是什麼動物而無法確定方法中寫什麼內容。這是我們就可以用抽象類進行實現。

<?php
//通過關鍵字abstract來聲明抽象類
abstract class Animal{
    protected $name;
    protected $age;
    //聲明該方法是抽象方法
    abstract public function sleep();
    abstract public function eat();
}

當父類的一些方法不能確定的時候,可以用abstract關鍵字來修飾該方法,稱爲抽象方法,而用abstract修飾的類稱爲抽象類。

基本語法:

abstract  class  類名{
    abstract  訪問修飾符   函數名(參數列表);
}

在開發中當我們想讓繼承該類的所有子類都重寫該類的方法,就可以用抽象方法來實現。抽象類就好比某個東西的架子也可以說是一個模板,有了模板就可以可以根據模板實現具體的功能。,而模板和具體事物的這種關係是通過繼承類傳遞的。就好比一臺電腦,通過模板來製作一臺臺電腦。而抽象類定義出來就是需要被繼承的。

<?php

abstract class Animal{
    protected $name;
    protected $age;

    abstract public function sleep();
    abstract public function eat();
}
class Dog extends Animal{

}
......結果........
Fatal error: Class Dog contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Animal::sleep, Animal::eat) in D:\mywamp\Apache24\htdocs\zendstudio\yunsuanfu\chouxiang.php on line 13

當一個類繼承了抽象類後,在子類中就需要實現父類的所有抽象方法上面報的錯誤就是說在父類中包含2個抽象方法,子類必須實現該抽象方法。而關於抽象類的特點可以總結如下:

  • 抽象類是不能被示例化的,不能通過抽象類來new一個對象,會報一個Cannot instantiate abstract class錯誤。
  • 抽象類中可以沒有抽象方法的,在抽象類中都是普通方法,但是類名是用abstract修飾的。

    <?php
    abstract class Animal{
        protected $name;
        protected $age;
        public function sleep(){
    
        }
        public function eat(){
    
        }
    }
    
  • 抽象類可以有普通的成員方法,屬性等

    <?php
    abstract class Animal{
        protected $name;
        protected $age;
    
        abstract public function sleep();
        abstract public function eat();
    
        public function cry(){
    
        }
    }   
    
  • 如果一個類中有抽象方法,那麼這個類必須是抽象類。

  • 抽象方法是沒有方法體的

    abstract public function eat();
    

    沒有{},也就是說沒有方法體。

  • 一個類繼承了抽象類,則子類必須實現抽象類的所有抽象方法,或者子類自己也聲明爲抽象類

接口

接口的初衷和抽象類是一樣的,不知道方法裏面怎麼實現的時候可以用接口來實現。而接口的定義是:給出一些沒有實現的方法,封裝到一起,到某個類要使用的時候,再根據具體情況把這些方法寫出來,接口的出現體現了高內聚低耦合的特點。

<?php

interface iTechnical{
    public function fly();
}

基本語法:

interface 接口名{
    //方法, 在接口中,所有的方法都不能有方法體, 即都是抽象方法
}

接口類和抽象類大致一樣,那接口具體是什麼呢?上面有說到抽象類就好比一臺筆記本的架子、模板,然後根據模板創建具體的筆記本,而沒有筆記本都有幾個usb接口,而接口類就好比這些筆記本上的接口,是一個擴展的實現。就像動物一樣都繼承了動物特有的特性喫,睡等,但是突然一隻動物在別的地方實現了寫字的本領,這種本領就是通過接口進行擴充的。

接口的命名一般是在類名的第一個字母是小寫的i開頭。在接口類中所有的方法都默認是抽象方法。所以並不需要寫abstract來聲明。

接口的實現

我們定義一個接口當然是讓別的類去實現的,這裏說的是實現,而不是繼承,接口和別的類之間是實現的關係,也就是類實現了某一接口,用implements關鍵字實現。

interface iTechnical{
    public function fly();
}

class Dog implements iTechnical{
    public function fly(){
        echo '<br>飛';
    }
}

當然在子類中必須實現接口中所有的方法。

關於接口的特點有以下幾點:

  • 接口類和抽象類一樣是不能被實例化的。
  • 接口中所有的方法都不能有主體,因爲都是抽象方法。
  • 一個類可以實現多個接口,逗號隔開(繼承只能是一個)

    class Dog implements 接口1,接口2{
    
    }
    
  • 接口中可以有屬性,但只能是常量 ,默認是public, 但不能用public 顯式修飾
  • 接口中的方法都必須是public的,接口就是用來繼承的所以用public,如果沒有寫修飾符,默認是public
  • 一個接口不能繼承其它的類,但是可以繼承別的接口

抽象和接口的區別

在PHP中繼承是單繼承的,也就是一個類最多隻能有一個父類,這種單繼承的機制可以保證類的純潔性。但是這種機制對子類功能擴展有一定的影響。

接口的出現可以說是對繼承的一種補充,繼承是層級的,不太靈活,而接口卻沒有它比抽象要靈活的多。並且實現接口在不打破繼承關係的前提下,對子類的功能進行擴充。
它們兩個的關係圖可以理解爲這樣:
這裏寫圖片描述

final的使用

在上面的介紹中每個類都是可以被繼承的,如果我們有一個類,我們不想讓子類去重寫裏面的某個方法,或者不想讓別的類去繼承該類,就可以用到關鍵字final。final中文意思:最後的,最終的,可以用來修飾類或者方法。

final可以修飾方法或者類,如果修飾方法,則表示該方法不可以被繼承類去重寫,如果final 修飾了一個類,則表示該類不可以被繼承。

基本語法:

final  class   類名{
}
class 類名{
    final 訪問修飾符 function 函數名(形參){}
}

修飾類和修飾方法。

<?php

    final class A{

    }

    class B extends A{

    }
    .....結果.....
    Class B may not inherit from final class (A)

不能繼承用final修飾的類。

<?php

    class A{
        final public function sum($num1,$num2){
            return $num1 + $num2;
        }

    }

    class B extends A{
        public function sum($num1,$num2){
            return $num1 + $num2;
        }
    }
    .....結果.....
    Cannot override final method A::sum()

從結果中可以看出來不能重寫用final定義的方法。

在使用final時,只能用來修飾類或者方法,不能修飾屬性。當一個類被final修飾後,類中的方法就不需要被final修飾,因爲類都不能繼承了,方法就不能重寫。同時final修飾的類是可以被實例化的。

如果一個方法被final修飾,是可以被繼承的,但是不能被重寫。

<?php

    class A{
        final public function sum($num1,$num2){
            return $num1 + $num2;
        }

    }

    class B extends A{

    }

    $b = new B();
    echo $b -> sum(1,2);
    .......結果.......
    3

在我們寫單例模式時,說過當時的單例模式是不完善的,可以通過繼承來得到不同的對象,在這裏我們使用final關鍵字修飾單例類,防止繼承,這樣就不能通過繼承的到別的對象。

類常量

類常量和普通的常量是一個概念,當不希望一個成員變量被修改,希望該變量的值是固定不變的。這時可以用const去修飾該成員變量,這樣這個變量就自動成爲常量。在類中的常量稱爲類常量。前面我們講過定義常量有兩種方式define()和關鍵字const,在類中必須使用const這種方式,使用define()是會報錯的。

<?php

    class A{
        const PI = 3.1415926;

        public function getPI(){
            return A::PI;
        }
    }

    $a = new A();
    echo $a -> getPI();
    echo '<br>';
    echo A::PI;
    ......結果......
    3.1415926
    3.1415926

類常量的命名一般是全部用大寫字母,中間用下劃線隔開,同時常量前面沒有$符號。常量必須在定義的時候就賦值。同時常量的前面不能有修飾符,默認是public。

常量的訪問形式

常量的訪問形式分爲兩種,一種是在類的內部進行訪問,一種是在類的外部進行訪問。

內部訪問

通過 類名::常量名進行訪問。

class A{
    const PI = 3.1415926;

    public function getPI(){
        return A::PI;//通過類名進行訪問
    }
}

通過 self::常量名進行訪問

class A{
    const PI = 3.1415926;

    public function getPI(){
        return self::PI;//通過類名進行訪問
    }
}

可以通過self進行訪問說明常量是屬於類的,並不屬於對象的。

外部訪問

通過類名::常量名訪問。

echo A::PI;

通過對象名::常量名訪問

$a = new A();
echo $a::PI;

不過推薦使用第一種,通過類名進行訪問。

如果一個類中有常量,則在繼承的時候常量也是可以被繼承的。同時常量的數據類型不能是對象。

總結

PHP中抽象和接口應用讓我們的更加清楚的把握需求的骨架,更好的搭建我們的代碼體系。同時利用抽象和接口降低代碼的耦合度。利於代碼的擴展。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章