【modernPHP專題(4)】抽象類與介面
抽象類
php5支援抽象類和抽象方法。類前加 abstract, 此類就成為抽象類,無法被例項化 ,此類天生就是用來被繼承的,給子類提供了一個類的模板;
類方法前加 abstract,是抽象方法,抽象方法不能有方法體;
總結下來抽象類有以下幾個特點:
- 類中有抽象方法,則此類一定要是抽象類;否則會報錯;
- 抽象類中的方法不一定都是抽象方法;
- 決定類是否能被例項化取決於其是不是抽象類,就算其類中的方法全部都是具體的方法而類本身是抽象方法的話,也不能被例項化
- 抽象類中全是具體方法,但是抽象類又不能被例項化,又想用其裡面的具體方法時,就可以通過繼承的方式或者通過類來呼叫;
- 抽象類是用作被繼承的類,所以其中的方法必須是public或者protected,又由於子類繼承其父類不能令其許可權越來越嚴格的規則,其子類也必須public或者protected;
- 如果是一個非抽象類繼承抽象類,就必須要完成抽象類中的所有的所有抽象方法(並且引數及其引數的型別要一樣),但如果是一個抽象類繼承一個抽象類的話可以不必完成抽象類中的抽象方法(抽象類繼承抽象類的目的就是擴充套件被繼承的抽象類);
以下程式碼摘抄自燕十八公益課堂
/* 春秋戰國時期,燕零七 飛行器專家,能工巧匠. 他寫了一份圖紙---飛行器製造術 飛行器祕製圖譜 1: 要有一個有力的發動機,噴氣式. 2: 要有一個平衡舵,掌握平衡 他的孫子問: 發動機怎麼造呢? 燕零七眼望夕陽: 我是造不出來,但我相信後代有人造出來 燕零七的構想在當時的科技造不出來,即這個類只能在圖紙化,無法例項化. ***/ // 此時這個類沒有具體的方法去實現,還太抽象. // 因此我們把他做成一個抽象類 abstract class FlyIdea { // 大力引擎,當時也沒法做,這個方法也實現不了,因此方法也是抽象的 public abstract function engine(); // 平衡舵 public abstract function blance(); /* 注意:抽象方法 不能有方法體 下面這樣寫是錯誤的 public abstract function blance() { } Fatal error: Abstract function FlyIdea::engine() cannot contain body */ } /* 抽象類不能 new 來例項化 下面這行是錯誤的 $kongke = new FlyIdea(); Cannot instantiate abstract class FlyIdea */ // 到了明朝,萬戶用火箭解決了發動機的問題 abstract class Rocket extends FlyIdea { // 萬戶把engine方法,給實現了,不再抽象了 public function engine() { echo '點燃火藥,失去平衡,嘭!<br />'; } // 但是萬戶實現不了平衡舵,因此平衡舵對於Rocket類來說,還是抽象的,類也是抽象的 // 此處由於繼承父類的也是抽象類,所以可以不必完成抽象類中的所有抽象方法; } /* 到了現代,燕十八親自制作飛行器 這個Fly類中,所以抽象方法,都已經實現了,不再是夢想. */ //到了這個類就必須要完成所有的抽象方法; class Fly extends Rocket{ public function engine() { echo '有力一扔<br />'; } public function blance() { echo '兩個紙翼保持平衡~~~'; } public function start() { $this->engine(); for($i=0;$i<10;$i++) { $this->blance(); echo '平穩飛行<br />'; } } } $apache = new Fly(); $apache->start();
介面
介面是 PHP物件之間的契約,抽象方法和靜態常量定義的集合。介面是一種特殊的抽象類,這種抽象類中只包含抽象方法和靜態常量。介面中沒有其它型別的內容。
總結如下:
- 介面是某類事物功能的抽象,本身方法預設全部是抽象的,不能有方法體;
- 介面的作用:指定了implements這些介面的類必須要包含接口裡面的方法以及方法裡面的引數,缺一不可。當然除了包含介面的這些方法外,可以增加其他的一些方法;
- 介面是可以繼承的
- 介面是一堆方法的說明,不能加屬性,但可以定義常量,定義的常量是不能被覆蓋,訪問方法是介面名::常量名 或者self::常量名
- 介面的方法必須是public;
- 類可以實現多介面
以下程式碼來自燕十八的公益課堂
<?php header("Content-type: text/html; charset=utf-8"); /* 類: 是某一類事物的抽象,是某類物件的藍圖. 比如: 女媧造人時,腦子中關於人的形象就是人類 class Human 如果,女媧決定造人時, 同時,形象又沒最終定稿時, 她腦子有哪些支離破碎的形象呢? 她可能會這麼思考: 動物: 吃飯 猴子: 奔跑 猴子: 哭 自己: 思考 小鳥: 飛 我造一種生物,命名為人,應該有如下功能 eat() run(); cry(); think(); 類如果是一種事物/動物的抽象 那麼 介面,則是事物/動物的功能的抽象, 即,再把他們的功能各拆成小塊 自由組合成新的特種 */ ; interface animal { const NAME = 'zxg'; //不能定義屬性,但可以定義常量; public function eat(); } interface monkey { public function run(); public function cry(); } interface wisdom { public function think(); } interface bird { public function fly(); } /* 如上,我們把每個類中的這種實現的功能拆出來 分析: 如果有一種新生物,實現了eat() + run() +cry() + think() ,這種智慧生物,可以叫做人. class Human implements animal,monkey,wisdom { } Human類必須要包含animal,monkey,wisdom接口裡面的方法,缺一不可,否則就會報錯 Class Human contains 4 abstract methods */ class Human implements animal, monkey, wisdom, bird { //這裡的介面數量可以隨意增加;增加了以後本類裡面的方法必須要有新增加的接口裡面的方法 public function eat() { echo "吃東西方法"; } public function run() { echo self::NAME; //可以通過self來訪問任意一個介面所定義的常量; echo '行走的方法'; } public function cry() { echo '哭的方法'; } public function think() { echo animal::NAME; //也可以通過 介面名 echo '思考的方法'; } public function smile() { echo "這是新增加的微笑方法"; } public function fly() { echo "這是新增加的介面bird裡面的fly方法"; } } $obj = new Human(); $obj -> think(); ?>
區別
- 對介面的使用是通過關鍵字implements。對抽象類的使用是通過關鍵字extends。當然介面也可以通過關鍵字extends繼承但最好還是通過implements實現。
- 介面中不可以宣告成員變數(包括類靜態變數),但是可以宣告類常量。抽象類中可以宣告各種型別成員變數,實現資料的封裝。
- 介面中只有抽象方法沒有具體的方法,抽象類可以有具體的方法。
- 介面中的方法預設都是public型別的,而抽象類中的方法可以使用protected,public來修飾(其實抽象類也可以宣告private方法的,不過沒有意義)。
- 一個類可以同時實現多個介面,但一個類只能繼承於一個抽象類。
使用介面還是抽象類?
- 如果要建立一個模型,這個模型將由一些緊密相關的物件採用,就可以使用抽象類。如果要建立將由一些不相關物件採用的功能,就使用介面。
- 如果必須從多個來源繼承行為,就使用介面(只有介面能繼承多個)。
- 如果知道所有類都會共享一個公共的行為實現,就使用抽象類,並在其中實現該行為。
應用場景舉例
更多的應用場景可以參考一些國外框架,比如YII
<?php //04.php /*** ====筆記部分==== 面向物件的一個觀點: 做的越多,越容易犯錯 抽象類{就定義類模板}--具體子類實現{china,japan,english} 介面: ***/ // 抽象的資料庫類 /* 創業做網站 到底用什麼資料庫?mysql, oracle,sqlserver,postgresql? 這樣:先開發網站,執行再說. 先弄個mysql開發著,正式上線了再換資料庫也不遲 引來問題: 換資料庫,會不會以前的程式碼又得重寫? 答:不必,用抽象類 開發者,開發時,就以db抽象類來開發. */ abstract class db { public abstract function connect($h,$u,$p); public abstract function query($sql); public abstract function close(); } /* // 下面這個程式碼有誤 // 因為子類實現時, connect和抽象類的connect引數不一致 class mysql extends db { public function connect($h,$h) { return true; } public function query($sql,$conn) { } public function close() { } } */ /* 下面這個mysql類,嚴格實現了db抽象類 試想: 不管上線時,真正用什麼資料庫 我只需要再寫一份如下類 class oracle extends db { } class mssql extends db { } class postsql extends db { } 業務邏輯層不用改? 為什麼不用改? 因為都實現的db抽象類. 我開發時,呼叫方法不清楚的地方,我就可以參考db抽象類. 反正子類都是嚴格實現的抽象類. */ class mysql extends db { public function connect($h,$h,$u) { return true; } public function query($sql) { } public function close() { } } /* 介面 就更加抽象了 比如一個社交網站, 關於使用者的處理是核心應用. 登陸 退出 寫信 看信 招呼 更換心情 吃飯 罵人 搗亂 示愛 撩騷 這麼多的方法,都是使用者的方法, 自然可以寫一個user類,全包裝起來 但是,分析使用者一次性使不了這麼方法 使用者資訊類:{登陸,寫信,看信,招呼,更換心情,退出} 使用者娛樂類:{登陸,罵人,搗亂,示愛,撩騷,退出} 開發網站前,分析出來這麼多方法, 但是,不能都裝在一個類裡, 分成了2個類,甚至更多. 作用應用邏輯的開發,這麼多的類,這麼多的方法,都暈了. */ interface UserBase { public function login($u,$p); public function logout(); } interface UserMsg { public function wirteMsg($to,$title,$content); public function readMsg($from,$title); } interface UserFun { public function spit($to); public function showLove($to); } /* 作為呼叫者, 我不需要了解你的使用者資訊類,使用者娛樂類, 我就可以知道如何呼叫這兩個類 因為: 這兩個類 都要實現 上述介面. 通過這個介面,就可以規範開發. */ /* 下面這個類,和介面宣告的引數不一樣,就報錯, 這樣,介面強制統一了類的功能 不管你有幾個類,一個類中有幾個方法 我只知道,方法都是實現的介面的方法. */ class User implements UserBase { public function login($u) { } } ?>