PHP基礎教程十一之封裝、繼承、多態

來源:互聯網
上載者:User

本節講解的內容

  • 封裝

  • 繼承

  • 多態

  • 重載

  • 重寫

前言

PHP的物件導向和JAVA的物件導向一樣,都分為三大特徵,封裝,繼承,多態。這三個特徵把物件導向進行了很多方面的最佳化。這三大特徵也是在開發物件導向的時候需要考慮的問題。

封裝

在物件導向中什麼是封裝呢?

封裝:把抽象出來的資料和對資料的操作封裝在一起,資料被保護在內部,程式的其他部分只有通過被授權的操作(成員方法),才能對資料進行操作。

上面有提到抽象,也就是把一類事物共有屬性和行為(方法)提取出來,形成一個模板(類), 這種研究問題的方法,我們稱為抽象。

就像我們的銀行帳號,不管是誰的帳號,都包括帳號,密碼,同時也有一些共同的方法,取款,存款,查詢餘額。我們用封裝的思想就是:

<?php    class Account{        public $account_no;        private $pwd;        private $balance;        public function __construct($account_no, $pwd = '123456', $balance = 0.0){            $this->account_no = $account_no;            $this->pwd = $pwd;            $this->balance = $balance;        }        //存款        public function deposit($amount, $pwd){            if($pwd == $this->pwd){                echo '<br> 存款成功';                $this->balance += $amount;            }else{                echo '<br> 密碼不正確';            }        }        //取款        public function withdraw($amount, $pwd){            if($pwd == $this->pwd){                if($this->balance >= $amount){                    echo '<br> 取款成功';                    $this->balance -= $amount;                }else{                    echo '<br> 餘額不足..';                }            }else{                echo '<br>密碼錯誤';            }        }        //查詢        public function query($pwd){            if($pwd == $this->pwd){                echo '<br> 帳號' . $this->account_no . ' 餘額=' . $this->balance;            }else{                echo '<br> 查詢密碼錯誤';            }        }    }    $account = new Account(123456789, 'hsp123', 2000000);    $account->deposit(30000, 'hsp123');    $account->query('hsp123');    $account->withdraw(99999900, 'hsp123');    $account->query('hsp123');

上面的代碼就是銀行業務的封裝,通過封裝代碼。我們操作,只需要調用裡面提供的方法就可以了,而不用管理裡面的業務是怎麼處理的。這是一個重要的思想。而我們實現封裝的方法是使用存取修飾詞,利用存取修飾詞的訪問特性,把不讓外部使用的屬性或方法封裝起來。而在上節中我們講過三種存取修飾詞。
那麼我們怎麼訪問protected 和 private 成員屬性呢?這裡有三種方法。

第一種

使用魔術方法__get() 和 __set() 來訪問

<?php    class Cat{        private $name;        private $age;        public function __construct($name,$age){            $this -> name = $name;            $this -> age = $age;        }        public function __set($name,$value){            //判斷類中有沒有這個屬性            if(property_exists($this,$name)){                $this -> $name = $value;            }else{                echo '沒有這個屬性';            }        }        public function __get($name){            return $this -> $name;        }    }    $cat = new Cat('小白',2);    $cat -> name = '小花';    echo $cat -> name;    ......結果......    小花

我們通過魔術方法set和get的特性來改變受保護的屬性的值,但是這種方法並不推薦使用。因為不夠靈活, 不可以對傳入的資料進行校正。而下面的方法就可以對傳入的資料進行校正。

第二種

為每個private或者protected 成員變數提供一對getXxx() 和 setXxx() 的方法。

<?php    class Cat{        private $name;        private $age;        public function __construct($name,$age){            $this -> name = $name;            $this -> age = $age;        }        public function setName($value){            if(is_string($value)){                $this -> name = $value;            }        }        public function getName($password){            if('12345' == $password){                return $this -> name;            }else{                echo '密碼不對';            }        }    }    $cat = new Cat('小白',2);    $cat -> setName('小花');    echo $cat -> getName('12345');    ......結果......    小花

使用這種方法可以對傳進去的資料進行判斷。我們推薦使用這種方法。

第三種

使用一個統一的方法,對屬性進行操作。

<?php    class Cat{        private $name;        private $age;        public function __construct($name,$age){            $this -> name = $name;            $this -> age = $age;        }        //顯示對象的資訊        public function showInfo(){            echo $this -> name . '的年齡是:' . $this-> age;        }    }    $cat = new Cat('小白',2);    $cat -> showInfo();    ......結果......    小白的年齡是:2

這三種方法,我們在開發中第二種和第三種用的比較多,當對單一的屬性操作的時候,可以使用第二種,當對多種屬性進行操作的時候,可以使用第三種。

繼承

在開發中使用繼承的情況太多了,繼承的出現,減少了代碼的冗餘性。是代碼看起來更加的清晰。那麼什麼是繼承呢?

<?php    //定義一個小學生,它有考試的方法,設定成績的方法    class Pupil {        public $name;        public $age;        private $grade;        public function __construct($name, $age){            $this->name = $name;            $this->age = $age;        }        public function showInfo(){            echo '<br> 學生的資訊如下:';            echo '<br> 學生的名字是:' . $this->name;            echo '<br> 學生的成績是:' . $this->grade;        }        //設定成績        public function setGrade($grade){            $this->grade = $grade;        }        public function testing(){            echo '<br> 小學生在考試.........';        }    }    //定義一個大學生,它有考試的方法,設定成績的方法    class Graduate {        public $name;        public $age;        private $grade;        public function __construct($name, $age){            $this->name = $name;            $this->age = $age;        }        public function showInfo(){            echo '<br> 學生的資訊如下:';            echo '<br> 學生的名字是:' . $this->name;            echo '<br> 學生的成績是:' . $this->grade;        }        //設定成績        public function setGrade($grade){            $this->grade = $grade;        }        public function testing(){            echo '<br> 大學生在考試.....';        }    }    //使用一下    $pupil1 = new Pupil('小明', 40);    $pupil1->testing();    $pupil1->setGrade(100);    $pupil1->showInfo();    echo '<br>';    //使用    $graduate1 = new Graduate('小華', 20);    $graduate1->testing();    $graduate1->setGrade(60);    $graduate1->showInfo();

在上面的代碼中我們可以看到兩個類中都有相同的屬性和方法,如果我們的代碼中有很多這樣的代碼,就會造成代碼的冗餘,更不利於我們的維護。而解決的最好的方法就是使用繼承,從而提高代碼的複用性。

繼承的文法:

class 方法名 extends 父類的方法名{}

繼承使用關鍵字extends進行繼承的。可以理解為代碼的複用,讓我們的編程更加靠近人的思維,當多個類存在相同的屬性(變數)和方法時,可以從這些類中抽取出父類,在父類中定義這些相同的屬性和方法,所有的子類不需要重新定義這些屬性和方法,只需要繼承父類就行了

<?php    //使用繼承來完成    class Student{        public $name;        public $age;        private $grade;        public function __construct($name, $age){            $this->name = $name;            $this->age = $age;        }        public function showInfo(){            echo '<br> 學生的資訊如下:';            echo '<br> 學生的名字是:' . $this->name;            echo '<br> 學生的成績是:' . $this->grade;        }        //設定成績        public function setGrade($grade){            $this->grade = $grade;        }    }    //這裡 extends 關鍵字就是表示 Pupil 類 繼承了 Student類    class Pupil extends Student{        public function testing(){            echo '<br> 小學生在考試.........';        }    }    class Graduate extends Student{        public function testing(){            echo '<br> 大學生在考試.........';        }    }    $pupil1 = new Pupil('小明', 40);    $pupil1->testing();    $pupil1->setGrade(100);    $pupil1->showInfo();    echo '<br>';    $graduate1 = new Graduate('小華', 20);    $graduate1->testing();    $graduate1->setGrade(60);    $graduate1->showInfo();

可以看到上面的代碼中把一樣的屬性和方法都出去到Student類(父類)中,我們再寫兩個類來通過extends這個關鍵字來繼承父類。
用來繼承的類稱為父類,而繼承這個類的類稱之為子類。
只要通過繼承,我們就能使用父類裡面的屬性和方法。但是如果父類的屬性或者方法被private修飾,則子類不能夠繼承,這就是protected和private的區別。

子類通過繼承得到父類的屬性和方法,那麼是不是就以為著把父類的代碼賦值了一份拷貝到子類呢?其實並不是的。繼承不是簡單把父類的屬性和方法定義拷貝一份到子類,而是建立了一個尋找的關係。而我們再訪問時,這種尋找關係是:

  1. 當某個對象去操作屬性和方法時,首先在本類去看有沒有對應的屬性和方法, 如果有,就判斷是否有存取權限,如果可以訪問則訪問,如果不可以訪問就報錯

  2. 當某個對象去操作屬性和方法時,首先在本類去看有沒有對應的屬性和方法, 如果沒有, 就會去尋找自己的父類, 父類有,再判斷是否可以訪問,可以訪問就訪問,不可以訪問就報錯。

  3. 這個尋找的邏輯到頂級類。。。。。

在繼承中還是有很多需要注意的地方:

  • 子類最多隻能繼承一個父類(指直接繼承),這和c++是不一樣的。

  • 子類可以繼承其父類(或者基類)的 public ,protected修飾的變數(屬性) 和 函數(方法)

  • 在建立某個子類對象時,預設情況下會自動調用其父類的建構函式(指在子類沒有自訂建構函式情況時)

    <?php    class A{        public function __construct(){            echo '父類的建構函式';        }    }    class B extends A{    }    //子類沒有定義建構函式,就會執行父類的建構函式。    $b = new B();    .....結果......    父類的建構函式
  • 如果在子類中需要訪問其父類的方法(構造方法/成員方法 方法的存取修飾詞是public/protected),可以使用父類::方法名(或者 parent::方法名 ) 來完成

    <?php    class A{        public function __construct(){            echo '父類的建構函式<br>';        }    }    class B extends A{        //子類定義了自己的建構函式,不會調用父類的建構函式。        public function __construct(){            parent::__construct();            echo '子類的建構函式<br>';        }    }    //子類沒有定義建構函式,就會執行父類的建構函式。    $b = new B();    .....結果......    父類的建構函式    子類的建構函式
  • 如果子類中的方法和父類方法相同,我們稱為方法重寫。

多態

多態通俗的講就是多種形態,就是指在物件導向中,對象在不同情況下的多種狀態(根據使用的上下文)PHP天生就是多態語言,同時PHP可以根據傳入的物件類型不同,調用對應對象的方法

在PHP中變數的定義不像其他語言在變數名前面定義類型,而是直接用$符號來定義,可以接受任意類型的值。這就為多態創造了條件。根據傳入的物件類型不同,調用對應對象的方法,我們也可以使用類型約束來對傳入的值進行約束。

類型約束

類型約束的基本概念是 PHP 5 可以使用類型約束。函數的參數可以指定必須為對象(在函數原型裡面指定類的名字),介面,數組(PHP 5.1 起)或者 callable(PHP 5.4 起)。如前PHP只支援這幾種。

<?php    class  MyClass    {         //傳進的參數約束為cat或者cat的子類               public function  test (Cat $cat ){            echo '對象<br>';        }        //參數是數組        public function  test_array (array  $arr ) {             print_r($arr);        }    }    class  Cat  {    }    $cat = new Cat();    $myClass = new MyClass();    $arr = array(1,2,4,5);    $myClass -> test($cat);    $myClass -> test_array($arr);    .....結果......    對象    Array ( [0] => 1 [1] => 2 [2] => 4 [3] => 5 )

如果要進行類型約束,在參數前面寫上類型就行了,可以試一下傳入的不是約束的類型,會報一個致命的錯誤。

多態的樣本:

<?php    //多態的案例    //定義動物的父類    class Anmial{        public $name;        public function __construct($name){            $this->name = $name;        }    }    //貓類    class Cat extends Anmial{        public function showInfo(){            echo '<br> 貓貓名字是' . $this->name;        }    }    //狗類    class Dog extends Anmial{        public function showInfo(){            echo '<br> 狗名字是' . $this->name;        }    }    //猴子類    class Monkey extends Anmial{        public function showInfo(){            echo '<br> 猴子名字是' . $this->name;        }    }    //定義事物    class Food{        public $name;        public function __construct($name){            $this->name = $name;        }    }    class Fish extends Food{        public function showInfo(){            echo '<br> ' . $this->name;        }    }    class Bone extends Food{        public function showInfo(){            echo '<br> ' . $this->name;        }    }    class Peach extends Food{        public function showInfo(){            echo '<br>' . $this->name;        }    }    //主人類    class Master{        public $name;        public function __construct($name){            $this->name = $name;        }        //主人可以餵食        //當我們的類型約束是父類時,可以接受 他的子類的對象執行個體        public function feed(Anmial $aniaml, Food $food){            echo '<br> 主人 ' . $this->name;            $aniaml->showInfo(); //使用多態,根據傳入的值不同,調用不同的方法。            echo '<br> 喂得食物是';            $food->showInfo();        }    }    $dog = new Dog('黃狗');    $cat = new Cat('花貓');    $monkey = new Monkey('猴');    $fish = new Fish('鯊魚');    $bone = new Bone('骨頭');    $peach = new Peach('桃子');    $master = new Master('小明');    $master->feed($dog, $bone);    echo '<br><br>';    $master->feed($cat, $fish);    echo '<br><br>';    $master->feed($monkey, $peach);

上面的代碼在主人的餵食方法中,使用類型約束,根據傳進去的對象不同,調用不同對象的方法。

重載

方法的重載

重載:簡單說,就是函數或者方法有相同的名稱,但是參數列表不相同的情形,這樣的同名不同參數的函數或者方法之間,互相稱之為重載函數或者方法。

上面的關於方法的重載可以理解為在一個類中,有兩個方法名一樣的函數,但是函數的參數是不一樣的,我們在調用的時候,系統會根據我們傳入的參數的不同,而自動的調用不同的函數,這就是重載,但是在PHP中一個類中不能有兩個方法名相同的函數,儘管你的參數不同,它會報一個Cannot redeclare的錯誤。那麼在PHP中就不能重載了嗎?其實可以的,利用魔術方法。

在上節中介紹魔術方法中有一個方法是當我們訪問不可訪問或不存在的方法時,系統自動調用的,也就是__call()方法。PHP中可以利用這個魔術方法進行重載

<?php    class Calculate{        //定義兩個方法,計算加法,注意兩個方法的方法名是不一樣的        private function add($a,$b,$c){             return $a + $b + $c;        }        private function add1($a,$b){            return $a + $b;        }        public function __call($name,$val_arr){            if($name == 'add'){                //得到數組裡面的參數,確定幾個參數                $num = count($val_arr);                if($num == 2){                    return $this -> add1($val_arr[0],$val_arr[1]);                }else if($num == 3){                    return $this -> add($val_arr[0],$val_arr[1],$val_arr[2]);                }            }        }    }    $calculate = new Calculate();    echo $calculate -> add(1,2);    echo '<br>';    echo $calculate -> add(1,2,3);    .....結果......    3    6

看到代碼有沒有被欺騙的感覺-_-,先把類中的兩個方法設定成private,這樣在類外就訪問不到,當我們在類外訪問add方法的時候,會調用魔術方法,然後通過傳入的數組的個數,確定參數的個數,從而在魔術方法中去掉用合適的方法。這就是PHP的方法重載。

屬性的重載

在PHP物件導向編程中,當你去給一個不存在的屬性賦值時,PHP預設會’動態’, 給你建立一個對應的屬性,這個稱為屬性重載。

<?php    class A{        //在類中只定義一個變數        public $name = '小明';    }    $a = new A();    echo '<pre>';    var_dump($a);    $a -> age = 12;    var_dump($a);    .....結果......    object(A)#1 (1) {      ["name"]=>      string(6) "小明"    }    object(A)#1 (2) {      ["name"]=>      string(6) "小明"      ["age"]=>      int(12)    }

從結果中可以看到,當我們給一個不存在屬性age賦值後,在輸出的類結構中出現了age這個屬性,這就是屬性的重載。

如果不想讓類的屬性自動增加,可以使用魔術方法__set()和__get()方法進行控制。

重寫

方法的重寫

在上面我們介紹了物件導向的繼承機制。而重寫是基於繼承才引發出來的概念。當一個類繼承了另外一個類後,在父類中有一個方法,子類繼承,但是在子類中覺得父類的方法不能滿足要求,需要子類在重寫定義一個和父類的方法一樣的方法進行重新的定義,稱為重寫。說白了就子類有一個方法,和父類(基類)的某個方法的名稱、參數個數一樣。

<?php    class Animal{        //動物都有吃這個行為,具體的要看是什麼動物        public function eat(){            echo '吃飯<br>';        }    }    class Cat extends Animal{        //對父類的方法進行重寫        public function eat(){            echo '貓在吃飯<br>';        }           }    $cat = new Cat();    $cat -> eat();    .....結果......    貓在吃飯

如果父類的方法中使用了類型約束,那麼子類的類型約束也必須一樣。

在方法的重寫中:

  1. 子類的方法的參數個數 ,方法名稱,要和父類方法的參數個數,方法名稱一樣

  2. 子類方法不能縮小父類方法的存取權限(可以大於可以等於)

注意:如果父類的方法名是private,則子類並不會進行重寫。

屬性的重寫

對於屬性的重寫也是,public 和 protected 可以被重寫,private 的屬性不能被重寫

<?php    class Animal{        public $name = '小花';        protected $age = 12;        private $sex = '雄';    }    class Cat extends Animal{        public $name = '小白';        protected $age = 4;        private $sex = '雄';    }    $cat = new Cat();    echo '<pre>';    var_dump($cat);    ......結果......    object(Cat)#1 (4) {      ["name"]=>      string(6) "小白"      ["age":protected]=>      int(4)      ["sex":"Cat":private]=>      string(3) "雄"      ["sex":"Animal":private]=>      string(3) "雄"    }

可以看到name和age被重寫了,private不能被重寫。

總結

物件導向中,封裝是一個很重要的思想,封裝的實現,可以降低代碼的耦合度,同時繼承的時候減少代碼的冗餘度,php的獨特語言結構讓多態也散發著光芒,而重寫的掌握,讓我們對繼承有了更深層次的瞭解。(ps:今天10.1號,祖國的生日,我要去給祖國母親慶生去了)

以上就是PHP基礎教程十一之封裝、繼承、多態的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.