看完就能掌握的PHP核心技術 - ​​​​​​​​物件導向

it阿布發表於2020-08-12

繼承和多型

類的組合與繼承

  • 假設我們有兩個類,一個 person,另外一個是 family;在 family 類中我們建立 person 類中的物件,並且我們把這個物件視為 family 類的一個屬性,並呼叫它的方法處理問題,那麼這種複用方式也稱為組合。
  • 類與類之間還有一種父與子的關係,子類可以繼承父類的屬性和方法,我們稱之為繼承。在繼承裡,子類擁有父類的屬性和方法,同時子類也有自己的屬性和方法。
<?php
class person{
    public $name = 'Tom';
    public $gender;
    static $money = 10000;
    public function __construct(){
        echo '這裡是父類',PHP_EOL;
    }

    public function say(){
        echo $this->name,"\t is ",$this->gender,"\r\n";
    }
}

class family extends person{
    public $name;
    public $gender;
    public $age;
    static $money = 1000;
    public function __construct(){
        parent::__construct();
        echo '這裡是子類',PHP_EOL;
    }

    public function say(){
        parent::say();
        echo $this->name,"\t is \t",$this->gender,",and is \t ",$this->age,PHP_EOL;
    }

    public function cry(){
        echo parent::$money,PHP_EOL;
        echo '% >_< %',PHP_EOL;
        echo self::$money,PHP_EOL;
        echo "(*^_^*)";
    }
}

$poor = new family();
$poor->name = 'Lee';
$poor->gender = 'female';
$poor->age = 25;
$poor->say();
$poor->cry();

返回結果

這裡是父類
這裡是子類
Lee  is female
Lee  is     female,and is    25
10000
% >_< %
1000
(*^_^*)% 
  • 組合和繼承都是提高程式碼可重用行的手段。在設計物件模型時,可以按照語義識別類之間的組合關係和繼承關係。

例如:

  • 繼承是一種 “是,像” 的關係,組合則是一種 “需要” 的關係 【父親和兒子應該是繼承關係,父親和家庭應該是組合關係 】
  • 組合偏重與和區域性的關係,繼承偏重與父與子的關係。
    在這裡插入圖片描述
  • 從方法複用的角度考慮,如果兩個類具有很多相同的程式碼和方法,我們就可以從這兩個類中抽象出一個父類,提供公共方法,然後兩個類作為子類,提供個性方法,這時繼承更好。
    PHP 核心技術 --​​​​​​​​
  • 組合的限制很少,組合之間的類可以關係很小 (體現為複用程式碼),設定沒有關係。

程式設計中

  • 繼承和組合的取捨往往都不是這麼直接明瞭,很難說出二者是 “像” 的關係還是需要的關係。甚至說把它拿到現實世界中建模,更加無法決定是繼承還是組合的關係了。這時,它該如何辦,有什麼標準?這個標準就是:低耦合

低耦合

  • 耦合是一個軟體結構內不同模組之間互聯程度的度量,也就是不同模組之間的依賴關係。
  • 低耦合是指模組和模組之間,儘可能地使模組間獨立存在;模組與模組之間的介面儘量少而簡單。現代的物件導向的思想不強調為真實世界建模,變得更加理性化一些,把目標放在解耦上。

解耦

  • 目的是為了解除模組與模組之間的依賴。
  • 繼承和組合二者在語義上難以區分,但是我們更傾向於使用組合。
  • 繼承存在的問題:
    • 繼承破壞封裝性;(鳥類為父類,而鴨子和鴕鳥作為子類,它們卻擁有飛翔的方法)
    • 繼承是緊耦合的,使得子類和父類捆綁在一起。組合僅是通過唯一介面和外部進行通訊,耦合度低於繼承。
    • 繼承擴充套件複雜,隨著繼承層數的增加和子類的增加,將涉及大量方法重寫。使用組合,可以根據型別約束,實現動態組合,減少程式碼。
    • 不恰當的使用繼承可能違反現實世界中的邏輯;

組合

<?php

class car{
    public function addoil(){
        echo "Add oil \r\n";
    }
}

class bmw extends car{
}
class benz{
    public $car;
    public function __construct(){
        $this->car = new car();
    }

    public function addoil(){
        $this->car->addoil();
    }
}
$bmw = new bmw();
$bmw->addoil();
$benz = new benz();
$benz->addoil();
  • 在建立組合物件時,組合需要一一建立區域性物件,這一點程度上增加了一些程式碼,而繼承不需要這一步,繼承擁有父類的方法,可以直接使用

如何使用繼承:

  • 精心設計專門用於被繼承的類,繼承樹的抽象層應該比較穩定,一般不多於三層;
  • 對於不是專門用於被繼承的類,禁止其被繼承,也就是使用 final 修飾符。使用 final 修飾符即可防止重要方法被非法覆寫,又能給編輯器尋找優化的機會;
  • 優先考了用組合關係提高程式碼的可重用性;
  • 子類是一直特殊的型別,而不只是父類的一個角色;
  • 子類擴充套件,而不是覆蓋或者使父類的功能失效;
  • 底層程式碼多用組合,頂層 / 業務層程式碼多用繼承。底層用組合可以提供效率,避免物件臃腫。頂層程式碼用繼承可以提高靈活性,讓業務使用更方便。

既要組合的靈活,又要繼承的簡潔

  • 多重繼承,一個類可以同時繼承多個父類,組合兩個父類的功能;缺點:多重繼承過於靈活,並且會帶來 “零星問題”,故為其使用帶來了不少困難,模型變得複雜起來。
  • traits php5.4 引入的新的語法結構,可以方便我們實現物件的擴充套件,是除 extend,implements 外的另外一種擴充套件物件的方式,traits 即可以使單繼承模式的語言獲得多重繼承的靈活,又可以避免多重繼承帶來的種種問題

traits 的用法

  • 通過在類中使用 use 關鍵字宣告要組合的 Trait 名稱,而具體某個 Trait 的宣告使用 trait 關鍵詞,Trait 不能直接例項化
<?php
trait Drive {
public $carName = 'BMW';
public function driving() {
echo "driving {$this->carName}\n";
}
}
class Person {
public function age() {
echo "i am 18 years old\n";
}
}
class Student extends Person {
use Drive;
public function study() {
echo "Learn to drive \n";
}
}
$student = new Student();
$student->study();
$student->age();
$student->driving();
Learn to drive
i am 18 years old
driving BMW
  • Student 類通過繼承 Person,有了 age 方法,通過組合 Drive,有了 driving 方法和屬性 carName。

如果 Trait、基類和本類中都存在某個同名的屬性或者方法,最終會保留哪一個呢

<?php
trait Drive {
public function hello() {
echo "hello 周伯通\n";
}
public function driving() {
echo "周伯通不會開車\n";
}
}
class Person {
public function hello() {
echo "hello 大家好\n";
}
public function driving() {
echo "大家都會開車\n";
}
}
class Student extends Person {
use Drive;//trait 的方法覆蓋了基類Person中的方法,所以Person中的hello 和driving被覆蓋
public function hello() {
echo "hello 新學員\n";//當方法或屬性同名時,當前類中的方法會覆蓋 trait的 方法,所以此處hello會覆蓋trait中的hello
}
}
$student = new Student();
$student->hello();
$student->driving();
hello 新學員
周伯通不會開車
  • 當方法或屬性同名時,當前類中的方法會覆蓋 trait 的 方法,而 trait 的方法又覆蓋了基類中的方法。

如果要組合多個 Trait,通過逗號分隔 Trait 名稱:

use Trait1, Trait2;
<?php
trait Hello {
public function sayHello() {
echo "Hello 我是周伯通\n";
}
}
trait World {
use Hello;
public function sayWorld() {
echo "hello world\n";
}
abstract public function getWorld();
public function inc() {
static $c = 0;
$c = $c + 1;
echo "$c\n";
}
public static function doSomething() {
echo "Doing something\n";
}
}
class HelloWorld {
use World;
public function getWorld() {
return 'do you get World ?';
}
}
$Obj = new HelloWorld();
$Obj->sayHello();
$Obj->sayWorld();
echo $Obj->getWorld() . "\n";
HelloWorld::doSomething();
$Obj->inc();
$Obj->inc();
Hello 我是周伯通
hello world
do you get World ?
Doing something
1
2

語言中得多型

  • 多型準確的含義是:同一類的物件收到相同訊息時,會得到不同的結果。而這個訊息是不可預測的。多型:顧名思義,就是多種狀態,也就是多種結果
  • 多型性是一種通過多種狀態或階段描述相同物件的程式設計方式。它真正的意義在於:實際開發中,只要關心一個介面或基類的程式設計,而不必關心一個物件所屬於的具體類

案例

  • 通過判斷傳入的物件所屬的類不同來呼叫其同名方法來實現 ’ 多型 ’
<?php
class employee{
protected function working(){
echo '本方法需要過載才能執行';
}
}
class teacher extends employee{
public function working(){
echo '教書';
}
}
class coder extends employee{
public function working(){
echo '敲程式碼';
}
}
function doprint($obj){
//get_class 獲取當前物件呼叫的類名
if(get_class($obj)=='employee'){
echo 'Error';
}else{
$obj->working();
}
}
doprint(new teacher());
doprint(new coder());
doprint(new employee());
  • 通過介面實現多型
<?php
interface employee{
public function working();
}
class teacher implements employee{
public function working(){
echo '教書';
}
}
class coder implements employee{
public function working(){
echo '敲程式碼';
}
}
function doprint(employee $i){
$i->working();
}
$a=new teacher;
$b=new coder;
doprint($a);
doprint($b);
  • 在這段程式碼中,doprint 函式的引數為一個介面型別的變數,符合 ’ 同一型別,不同結果 ’ 這一條件,具備多型的一般特徵。

總結

  • 多型指同一類物件在執行時的具體化。
  • php 語言是弱型別的,實現多型更簡單,更靈活。
  • 型別轉換不是多型。
  • php 中父類和子類看作 ’ 繼父 ’ 和’ 繼子 ’ 關係,他們存在繼承關係,但不存在血緣關係,因此子類無法向上轉型為父類,從而失去多型最典型的特徵。
  • 多型的本質就是 if – else – ,只不過實現的層次不同。

相關文章