PHP經驗總結 - 聊聊面向對象

簡述

「如今大夥都在講面向對象編程,可是咱們也得先找着一個對象是不?否則怎麼面向對象?怎麼編程?」 --- 笑話一則,可是理不虧,要搞P面向對象編程,咱們起碼要先搞懂對象(還有類)是什麼?只有瞭解它,理解它,你才能駕馭它。作編程的不能瞎搞,邏輯嚴謹清晰最重要,要明白咱們在作什麼?我須要作什麼?我該怎麼作?接下來,我來談談PHP類和對象的認知,而後說一下咱們應該怎麼用它們。php

走進 PHP 類和對象

簡述PHP類和對象

  • 初學者的角度,能夠認爲類就是屬性+函數。程序員

  • 類是面向對象程序設計的基本概念,通俗的理解「類」就是對現實中某一個種類的東西的抽象。數據庫

  • 好比:汽車能夠抽象爲一個類,汽車擁有名字、輪胎、速度、重量等屬性,能夠有換擋、前進、後退等操做方法。編程

  • 一般定義一個汽車類的方法爲:設計模式

<?php
    // 定義一個叫Car的類
    class Car {
        $name = '汽車';// 這個類有個變量$name,它的值爲「汽車」
        // 這個類同時還有一個函數方法叫getName(),它能實現返回這個Car的類裏面的變量$name的值
        function getName() {
            return $this->name;
        }
    }

?>
  • 類是一類東西的結構描述,而對象則是一類東西的一個具體實例。函數

  • 例如:汽車這個名詞能夠理解爲汽車的總類,但這輛寶馬汽車則是一個具體的汽車對象。學習

  • 對象能夠經過new關鍵字進行實例化:this

<?php

    $car = new Car(); // 實例化Car這個類
    echo $car->getName();// 調用Car這個類裏面的getName()方法並輸出結果(由於類被實例化了,因此這個類裏面的函數、方法、變量、常量等均可以調用使用)

?>
  • 類與對象看起來比較類似,但實際上有本質的區別:類是抽象的概念,對象是具體的實例。設計

  • 類可使程序具備可重用性。指針

如何建立一個類

  • 類經過關鍵字class開頭,而後是類名與花括號,在花括號中定義類的屬性與方法。

  • 類名必須是字母或下劃線開頭,後面緊跟若干個字母、數字或下劃線,類名最好可以表意,能夠採用名詞或者英文單詞。

  • 定義一個類,你能夠這麼幹:

<?php

    // 定義一個類Car
    class Car {
        // 定義屬性變量$name
        public $name = '汽車';
    
        // 定義方法getName()
        public function getName() {
            // 方法內部可使用$this僞變量調用對象的屬性或者方法
            return $this->name;
        }
    }

?>
  • 要建立一個類的實例,可使用new關鍵字建立一個對象,下面介紹2種建立類的方法:

<?php

    // 實例化類Car
    $car = new Car(); 
    //也能夠採用變量來建立類Car
    $className = 'Car';
    $car = new $className();

?>
  • 注意:類的定義和建立是有本質上的區別的,定義類只是至關於創造了一臺汽車,而建立類則是你要去開這臺汽車,可是你並不知道這臺汽車是否存在(類是否已被定義),因此在使用類以前那你必需要確保你要用的類存在(已被定義)。

類的屬性

1.在類中定義的變量稱之爲屬性,一般屬性跟數據庫中的字段有必定的關聯,所以也能夠稱做「字段」。
2.屬性聲明是由關鍵字`public`,`protected`或者`private`開頭,後面跟一個普通的變量聲明來組成。
3.屬性的變量能夠設置初始化的默認值,默認值必須是常量。
4.訪問控制的關鍵字表明的意義爲:
  public:公開的
  protected:受保護的
  private:私有的

下面有一個案例,能夠參考一下:

<?php

    class Car {
        //定義公共屬性
        public $name = '汽車';
    
        //定義受保護的屬性
        protected $corlor = '白色';
    
        //定義私有屬性
        private $price = '100000';
    }

?>
  • 類的屬性默認都爲public,外部能夠訪問。

  • 類的屬性通常經過 -> 對象操做符來訪問對象的屬性或者方法,對於靜態屬性則使用 : : 雙冒號進行訪問。

  • 當在類成員方法內部調用的時候,可使用$this僞變量調用當前對象的屬性。

下面是類屬性調用的案例,也有一些錯誤的屬性調用:

<?php

    $car = new Car();
    echo $car->name;   // 調用對象的屬性
    echo $car->color;  // 錯誤 受保護的屬性不容許外部調用
    echo $car->price;  // 錯誤 私有屬性不容許外部調用

?>
  • 受保護的屬性與私有屬性不容許外部調用,在類的成員方法內部是能夠調用的。以下:

<?php

    class Car{
        private $price = '1000';
        public function getPrice() {
            return $this->price; // 內部訪問私有屬性
    ​    }
    }

?>

定義類的方法

  • 方法就是在類中的function,不少時候咱們分不清方法與函數有什麼差異 :

    1.在面向過程的程序設計中function叫作函數 。
    2.在面向對象中function則被稱之爲方法 。

    注:同屬性同樣,類的方法也具備publicprotected以及private的訪問控制。
    *訪問控制的關鍵字表明的意義爲:

    public:公開的
       protected:受保護的
       private:私有的
  • 咱們能夠這樣定義方法:

<?php

    class Car {
        // 顯然我定義了一個公共方法,類的外部也能夠調用 
        public function getName() {
            return '汽車';
        }
    ​}
    $car = new Car();
    echo $car->getName();

?>
  • 使用關鍵字static修飾的,稱之爲靜態方法,靜態方法不須要實例化對象,能夠經過類名直接調用,操做符爲雙冒號 : :

<?php

    class Car {
        // 顯然我定義了一個公共的靜態方法
        public static function getName() {
            return '汽車';
        }
    ​}
    echo Car::getName(); // 結果爲「汽車」

?>

構造函數和析構函數

  • top:這個其實我本身把握也不是很大,可能本身使用的比較少,析構方法和構造方法這些用的妙經常出如今高級工程師之手,都是一些項目底層代碼裏面常常用到的,而我之因此不多接觸,顯然易見我仍是菜鳥,因此我應該繼續好好學習。

  • 定義:PHP5能夠在類中使用__construct()定義一個構造函數,具備構造函數的類,會在每次對象建立的時候調用該函數,所以經常使用來在對象建立的時候進行一些初始化工做。(這個的意思大概就是說構造函數每每在對象建立以前就調用該函數建立一些系統必須的共用對象或者類方法,還有完成一些初始化工做,因此這些構造函數每每都是一些最最最底層的東西)。

  • 下面有一個很很很簡單構造函數案例:

<?php

    class Car {
       // 這個就是大名鼎鼎的構造函數,通常在系統執行一些初始化工做
       function __construct() {
           print "構造函數被調用\n";
       }
    }
    $car = new Car(); //實例化類Car的時候 會自動調用構造函數__construct,這裏會輸出一個字符串

?>
  • 若是在子類中定義了__construct(),則不會調用父類的__construct()。

  • 若是須要同時調用父類的構造函數,須要使用 parent : : __construct() 顯式的調用。

  • 案例(可能有點繞):

<?php
    // 注意,這個類Car是父級類
    class Car {
       function __construct() {
           print "父類構造函數被調用\n";
       }
    }
    // 這個類Truck是Car的子類,由於它繼承(extexds)了父類Car
    class Truck extends Car {
       function __construct() {
           print "子類構造函數被調用\n";
           parent::__construct();
       }
    }
    $car = new Truck();// 結果顯示:子類構造函數被調用\n父類構造函數被調用\n

?>
  • 一樣,PHP5支持析構函數,使用__destruct()進行定義,析構函數指的是當某個對象的全部引用被刪除,或者對象被顯式的銷燬時會執行的函數。

案例:

<?php

    class Car {
       function __construct() {
           print "構造函數被調用 \n";
       }
       function __destruct() {
           print "析構函數被調用 \n";
       }
    }
    $car = new Car(); // 實例化時會調用構造函數
    echo '使用後,準備銷燬car對象 \n';
    unset($car); // 銷燬時會調用析構函數

?>
  • 當PHP代碼執行完畢之後,會自動回收與銷燬對象,所以通常狀況下不須要顯式的去銷燬對象。

Static靜態關鍵字

  • 靜態屬性與方法能夠在不實例化類的狀況下調用,直接使用 類名::方法名 的方式進行調用。

  • 靜態屬性不容許對象使用->操做符調用:

<?php

    class Car {
        private static $speed = 10;
        
        public static function getSpeed() {
            return self::$speed;
        }
    }
    echo Car::getSpeed();  //調用靜態方法,獲取對象Car裏面的靜態屬性$speed的值,並輸出

?>
  • 靜態方法也能夠經過變量來進行動態調用:

<?php

    $func = 'getSpeed';
    $className = 'Car';
    echo $className::$func();  //動態調用靜態方法

?>
靜態方法中,$this僞變量不容許使用。可使用self,parent,static在內部調用靜態方法與屬性:
<?php

    // 父類Car
    class Car {
        // 靜態屬性
        private static $speed = 10;
        // 靜態方法
        public static function getSpeed() {
            return self::$speed;
        }
        // 靜態方法
        public static function speedUp() {
            return self::$speed+=10;
        }
    }
    
    // 子類BigCar繼承父類Car
    class BigCar extends Car {
        // 靜態方法
        public static function start() {
            parent::speedUp();
        }
    }
     
    BigCar::start();// 實例化調用BigCar類的statr()方法獲取父類中的靜態方法speedUp,實現靜態屬性$speed的值加10
    echo BigCar::getSpeed(); // 解釋太長了,本身腦補吧

?>

訪問控制

訪問控制經過關鍵字public,protected和private來實現。
  • 被定義爲公有的類成員(public)能夠在任何地方被訪問。

  • 被定義爲受保護的類成員(protected)則能夠被其自身以及其子類和父類訪問。

  • 被定義爲私有的類成員(private)則只能被其定義所在的類訪問。

  • 類屬性必須定義爲公有、受保護、私有之一。

  • 爲兼容PHP5之前的版本,若是採用 var 定義,則被視爲公有。

  • 案例:

<?php

    class Car {
        $speed = 10; // 錯誤 屬性必須定義訪問控制
        public $name;   // 定義共有屬性
    }

?>
  • 類中的方法能夠被定義爲公有、私有或受保護。

  • 若是沒有設置這些關鍵字,則該方法默認爲公有:

<?php

    class Car {
    ​    //默認爲共有方法
        function turnLeft() {
        }
    }

?>
  • 若是構造函數定義成了私有方法,則不容許直接實例化對象了,這時候通常經過靜態方法進行實例化,在設計模式中會常用這樣的方法來控制對象的建立,好比單例模式只容許有一個全局惟一的對象:

<?php

    class Car {
        // 顯然這是一個私有的構造函數
        private function __construct() {
            echo 'object create';
        }
        // 這是一個私有的屬性
        private static $_object = null;
        // 這是一個公共方法
        public static function getInstance() {
            if (empty(self::$_object)) {
                self::$_object = new Car(); // 內部方法能夠調用私有方法,所以這裏能夠建立對象
            }
            return self::$_object;
        }
    }
    // $car = new Car(); // 這裏不容許直接實例化對象
    $car = Car::getInstance(); // 經過靜態方法來得到一個實例

?>

對象繼承

  • 繼承是面向對象程序設計中經常使用的一個特性,汽車是一個比較大的類,咱們也能夠稱之爲基類,除此以外,汽車還分爲卡車、轎車、東風、寶馬等,由於這些子類具備不少相同的屬性和方法,能夠採用繼承汽車類來共享這些屬性與方法,實現代碼的複用。

  • 在代碼中,實際上就是類的繼承,ClassA extends ClassB,就是這麼簡單,可是這爲咱們開發提供了一個對象的重用性的特質,使得咱們在開發上獲得更好便利。

  • 對象(個人理解就是類)的繼承,就是函數方法調用的通道和數據接口的使用,實際使用就是這麼的一個體驗。(這僅僅是個人觀點,歡迎你們指正個人觀點,同時歡迎你們發表你的觀點。)

重載

  • PHP中的重載指的是動態的建立屬性與方法,是經過魔術方法來實現的。

  • 屬性的重載經過__set,__get,__isset,__unset來分別實現對不存在屬性的賦值、讀取、判斷屬性是否設置、銷燬屬性。

  • 關於重載,有如下這個案例能夠看一下:

<?php

    class Car {
        // 顯然這是一個私有屬性
        private $ary = array();
        // 魔法方法 __set
        public function __set($key, $val) {
            $this->ary[$key] = $val;
        }
        //  魔法方法__get
        public function __get($key) {
            if (isset($this->ary[$key])) {
                return $this->ary[$key];
            }
            return null;
        }
        //  魔法方法__isset
        public function __isset($key) {
            if (isset($this->ary[$key])) {
                return true;
            }
            return false;
        }
        //  魔法方法__unset
        public function __unset($key) {
            unset($this->ary[$key]);
        }
    }
    $car = new Car();
    $car->name = '汽車';  //name屬性動態建立並賦值
    echo $car->name;

?>
  • 方法的重載經過__call來實現,當調用不存在的方法的時候,將會轉爲參數調用__call方法,當調用不存在的靜態方法時會使用__callStatic重載。

<?php

    class Car {
        public $speed = 0;
        // 顯然這是一個魔法方法__call(),實現方法的重載
        public function __call($name, $args) {
            if ($name == 'speedUp') {
                $this->speed += 10;
            }
        }
    }
    $car = new Car();
    $car->speedUp(); // 調用不存在的方法會使用重載
    echo $car->speed;

?>

對象的高級特性

  • 對象比較,當同一個類的兩個實例的全部屬性都相等時,可使用比較運算符==進行判斷,當須要判斷兩個變量是否爲同一個對象的引用時,可使用全等運算符===進行判斷:

<?php

    class Car {
        echo "===";
    }
    $a = new Car();
    $b = new Car();
    if ($a == $b) echo '==';   //true
    if ($a === $b) echo '==='; //false

?>
  • 對象複製,在一些特殊狀況下,能夠經過關鍵字clone來複制一個對象,這時__clone()方法會被調用,經過這個魔術方法來設置屬性的值。

<?php

    class Car {
        public $name = 'car';
        // 這是一個魔法方法__clone()方法
        public function __clone() {
            $obj = new Car();
            $obj->name = $this->name;
        }
    }
    $a = new Car();
    $a->name = 'new car';
    // 經過關鍵字clone來複制一個對象
    $b = clone $a;
    var_dump($b);

?>
  • 對象序列化,能夠經過serialize()方法將對象序列化爲字符串,用於存儲或者傳遞數據,而後在須要的時候經過unserialize()將字符串反序列化成對象進行使用。

<?php

    class Car {
        public $name = 'car';
    }
    $a = new Car();
    $str = serialize($a); //對象序列化成字符串
    echo $str.'<br>';
    $b = unserialize($str); //反序列化爲對象
    var_dump($b);

?>

小結

  • 人人都在喊面向對象編程,可是卻不是人人都懂面向對象編程,這是一個很模糊很模糊的概念,模糊到不少人都不知道怎麼定義它們。

  • 到如今爲止尚未人可以正式定義它們,這一切都須要靠本身,怎麼去形容它?我通常用形容來對待什麼是面向對象,類就像一個汽車類,而他的對象就是那些跑車小轎車什麼的。

  • 類和對象要切記對象是類的具體表現形式(基本存在),好比寶馬車之於汽車類的意義。

總結

  • 面向對象編程(OOP)是一種設計範式,同時也是一種程序開發方法。它視對象爲程序的基本單元,將程序和數據封裝在其中,以提升程序的重用性、靈活性和可擴展性。

  • 類是對象的抽象組織,對象是類的基本存在。

  • 對象和類的概念及二者的關係:
    1.類是定義一系列屬性和操做的模版,而對象則是把屬性進行具體化,而後交給類處理。
    2.對象就是數據,對象自己不包含方法。可是對象有一個「指針」指向一個類而這個類裏能夠有方法。
    3.方法描述不一樣屬性會致使不一樣的表現。
    4.類和對象是不可分割的,有對象就一定有一個類與其對應,不然這個對象也就沒有意義了。(可是有一種特殊狀況:由標量進行強制類型轉換的object,沒有一個類與他相對應,此時PHP中一個稱爲「孤兒」sidClass類就會收留這個對象)。

  • 關於類的繼承和組合的總結:首先繼承是一種「像」或「是」的關係。而組合則是一種「須要」的關係。好比:
    1.若是兩個或者多個類有相同的代碼和方法時,大可把它們都抽象(離)出來造成一個父類,而後它們這些有相同代碼或方法的類做爲子類 去繼承它(哪些共有的代碼)。
    2.組合相比繼承簡單,組合的類能夠是有關係(體現爲複用代碼不多),甚至不要緊(複用的方法或代碼)。

  • 在編程中,耦合是一種軟件結構內不一樣模塊代碼之間互連程度的度量,也就是不一樣模塊之間的依賴關係。(這個代碼開發時要考慮代碼可擴展性和易維護性,否則很容易牽一髮而動全身的問題)。

  • 低耦合是指模塊與模塊之間儘量地使模塊間獨立存在,模塊與模塊之間的接口儘可能少而簡單。(這個就是說咱們寫的代碼要儘量的實現靠本身就能實 現功能,不須要靠別人,由於別人不必定靠得住,誰知道哪一天它就被幹掉了呢?)。

  • 解耦是指要接觸模塊與模塊之間的依賴。

  • 底層代碼多用組合,頂層/業務層代碼多用繼承:
    1.底層用組合能夠提升效率,避免臃腫。
    2.頂層代碼用繼承能夠提升靈活性,讓業務使用更加方便。

  • 面對對象的多態?
    1.多態是指同一類對象在運行時的具體化。
    2.PHP語言是弱類型,實現多態更簡單更靈活。
    3.類型轉換不是多態。
    4.PHP中父類和子類看做「繼父」和「繼子」關係,他們存在繼承關係,可是不存在血緣關係,所以子類沒法向上轉爲父類,從而失去多態最典型的特徵。
    5.多態的本質就是一個if...else,只不過實現的層級不一樣。

  • 面向對象的接口
    1.接口是做爲一種規範和契約的存在:做爲規範,接口應該保證可用性;做爲契約,接口應該保證可控性。
    2.接口只是一個聲明,一旦使用interface關鍵字,就應該實現它。能夠由程序員實現(外部接口),也能夠由系統實現(內部接口)。
    3.接口自己什麼都不作,可是它能夠告訴咱們它能作什麼。
    4.PHP接口存在兩點不足:沒有契約的限制;缺乏足夠多的內部接口。

  • 面向對象設計五大原則:
    1.單一職責原則(SRP):避免同一職責分散到不一樣類中,避免一個類承擔太多職責(個人建議就是每一個類儘量簡單,單純實現一個功能,並且代碼量儘量精短,數據加工的方法能夠抽離出來)。
    2.接口隔離原則(ISP):使用多個接口比使用單個接口好(一個類對另一個類的依賴性(影響)是創建在最小的接口上,避免接口污染,避免爲接口添加沒必要要的職責)。
    3.開放-封閉原則(OCP):模塊在擴展性方面應該是開放的,而模塊的更改性應該是封閉的(模塊的行爲必須是開放的,支持擴展而不是僵化的;對模塊的功能進行擴展時,不該該影響或大規模地影響已有程序模塊)。
    4.替換原則(LSP):子類必須可以替換它們的父類,並表明其出如今任何地方(父類的方法都要在子類中實現或者重寫,而且派生類只實現其抽象類中聲明的方法,不該該給出多餘的方法定義或實現;在客戶端程序中只應該使用父類對象而不該當直接使用子類對象,這樣能夠實現運行期綁定(動態多態))。
    5.依賴倒置原則(這個真不熟):將依賴關係倒置爲依賴接口(上層模塊不該該依賴下層模塊,它們共同依賴同一個抽象;抽象不能依賴於具體,具體應該要依賴抽象)。

  • 面向對象優勢:
    1.新成員的加入和融入不在困難,高難度抽象有利於高度總結。
    2.代碼即文檔,團隊中任何人均可以輕鬆地得到產品的各個模塊的基本信息,而不須要讀大量代碼。

  • 注:建議你們看看《PHP核心技術與最佳實踐》這本書,由列旭鬆和陳文著做,機械工業出版社2013出版的。

相關文章
相關標籤/搜索