網路上對抽象類、介面、性狀的總結都是停留在概念層面,程式碼也都是相似的。我覺得大部分作者自己都不知道怎麼使用,這裡從程式碼的層面簡單總結一下它們常見使用場景,加深理解,記住一切皆型別就對了。
一、介面(interface)
對具體的實現類的型別進行約束,只要實現了介面的方法,原來的類就會多一種型別。介面不能直接例項化,也不能包含常量,可以繼承。如果你編寫的類有大量相同的行為,那麼你可以把它定義成介面。
實現介面
<?php // 介面 interface Animal { public function eat(); } // 實現介面的具體類 class Dog implements Animal { public function eat() { print "dog eat \n"; } } // 例項化類 $dog = new Dog(); var_dump($dog instanceof Animal); var_dump($dog instanceof Dog); // 輸出: # bool(true) # bool(true)
可以看出,當一個類實現了介面以後,原來的類就會多一種型別,可以利用介面的特性進行引數約束和型別轉換(協變和逆變)。
使用場景
實現多型、里氏替換、面向介面程式設計。
<?php // 介面 interface Animal { public function eat(); } // 實現介面的具體類 class Dog implements Animal { public function eat() { print "dog eat \n"; } } // 實現介面的具體類 class Cat implements Animal { public function eat() { print "cat eat \n"; } } function call(Animal $animal) { print $animal->eat(); } call(new Dog()); call(new Cat());
可以看出,對於同一個函式call,引數設定為介面型別(Animal型別),任何實現介面的類(Cat、Dog)都會多出一種型別(Animal),所以函式能正常接收和呼叫,實現多型和里氏替換的效果。
二、抽象類(abstract class)
對共性保留,對差異開放,即求同存異,只要實現了抽象類的抽象方法,原來的類就會多一種型別,抽象類不能被直接例項化,可以繼承。如果你編寫的類有大量相同的程式碼,可以提取到抽象類中複用。
實現抽象類
<?php // 抽象類 abstract class Animal { public function animalShout() { print "hello, "; $this->shout(); } abstract function shout(); } // 實現抽象類的具體類 class Dog extends Animal { function shout() { print "dog shout \n"; } } // 實現抽象類的具體類 class Cat extends Animal { function shout() { print "cat shout \n"; } } $dog = new Dog(); $dog->animalShout(); $cat = new Cat(); $cat->animalShout(); # 輸出 # hello, dog shout # hello, cat shout
可以看出,Cat類和Dog類都呼叫了同一個方法
animalShout
列印hello
(求同),然後各自實現了各自的shout
方法(存異),可以使用抽象類的這個特性實現多型和里氏替換的效果。
三、性狀(trait)
將公共的程式碼提取出來,在多個地方進行嵌入複用。性狀不能被直接例項化,性狀中可以巢狀性狀。如果你編寫的業務類中有和業務無關的程式碼,比如:實現單例和一些工具方法,你可以提取到性狀中複用。
- 使用性狀
<?php
// 實現單例
trait Singleton {
private static $_instance = null;
/**
* @return static
*/
public static function getInstance() {
self::$_instance || self::$_instance = new self();
return self::$_instance;
}
private function __clone() {
}
private function __construct() {
}
}
class User {
// 使用性狀
use Singleton;
public $age;
/**
* @param $age
* @return $this
*/
public function setAge($age) {
$this->age = $age;
return $this;
}
/**
* @return mixed
*/
public function getAge() {
return $this->age;
}
}
print User::getInstance()->setAge(20)->getAge();
print User::getInstance()->setAge(21)->getAge();
# 輸出
# 20
# 21
可以看出,只需要嵌入性狀,就可以在類中呼叫性狀的方法,實現單例,任何需要單例的地方只需要嵌入就行,避免寫重複的程式碼。
本作品採用《CC 協議》,轉載必須註明作者和本文連結