PHP的介面自始至終一直被爭議,有人說接口很好,有人說接口像雞肋。首先要明白,好喝不好的判斷標準是什麼。無疑,這是和Java/C++相比。在上面的例子中,以及討論了PHP的介面在「面向契約程式設計」中是不足的,並沒有起到應有的作用。
其實,machine類別的宣告應該在plain類別前面。介面提供了一套規範,這是系統提供的,然後machine類別提供一組針對介面的API並實現,最後才是自訂的類別。在Java裡,接口之所以盛行(多線程的runable接口,容器的collection接口等)就是因為系統為我們做了前面兩部分的工作,而程序員,只需要去寫具體的實現類,就能保證介面可用可控。
為什麼要用介面?介面到底有什麼好處?介面本身並不提供實現,只是提供一個規範。如果我們知道一個類別實作了某個接口,那麼就知道了可以呼叫該接口的那些方法,我們只需要知道這些就夠了。在
PHP中,介面的語意是有限的,使用介面的地方不多,PHP中介面可以淡化為設計文檔,起到一個團隊基本契約的作用,程式碼如下所示:
<?php interface Cache { /** * describe:缓存管理,项目经理定义接口,技术人员负责实现 */ const maxKey = 10000; //最大换存量 public function getCache($key); //获取缓存 public function setCache($key,$value); //设置缓存 public function flush(); //清空缓存 }
由於PHP是弱類型,且強調靈活,所並不建議大規模使用接口,而是僅在部分“內核”代碼中使用接口,因為PHP中的接口已經失去很多接口應該具有的語義。從語義上考慮,可以更多地使用抽象類別。至於抽象類別和介面的比較,不再贅述。
另外,PHP5對物件導向的特種做了許多增強,其中就有一個SPL(標註PHP函式庫)的嘗試,SPL中實作一些接口,其中最主要的就是iterator迭代器接口,透過實現這個接口,就能使對象能用於foreach結構,從而在使用形式上比較統一。例如SPL中一個DirectoryIterator類,這個類別在整合SplFileInfo類別的同時,實作Iterator、Traversable、SeekableIterator這三個接口,那麼這個類別的實例可以獲得父類別SplFileInfo的全部功能外,還能夠實作Iterator介面所展示的那些操作。
Iterator介面的原型如下:
* current() This methodreturns the current index's value. You are solely responsible for tracking what thecurrent index is as the interface does not do this for you. *key() This method returns the value of the current index's key. For foreach loops this is extremely important so that the key value can be populated. *next() This method moves the internal index forward one entry. *rewind() This method should reset the internal index to the first element. *valid() This method should return true or false if there is a current element. It is called after rewind() or next().
如果一個類別宣告了實作Iterator,就必須實作這五個方法,如果實作了這五個方法,那麼就可以很容易地對這個類別的實例進行迭代。這裡,DirectoryIterator類之所以拿來就能用,是因為系統已經實現了Iterator接口,所以可以像下面這樣使用:
<?php $dir = new DirectoryIterator(dirname(FILE)); foreach ($dir as $fileInfo) { if(!$fileInfo->isDir()) { echo $fileInfo->getFilename(),"\t",$fileInfo->getSize(),PHP_EOL; } }
可以想像,如果不用DirectoryIterator類,而是自己實現,不但代碼量增加了,循環時候的風格也不統一了。如果自己寫的類別也實作了Iterator接口,那麼就可以像Iterator那樣運作。
為什麼一個類別只要實作了Iterator迭代器,其物件就可以被用做foreach的物件呢?其實原因很簡單,在對PHP實例物件使用foreach語法時,會檢查這個實例有沒有實現Iterator接口,如果實現了,就會透過內建方法或使用實作類別中的方法模擬foreach語句,這是不是和前面提到的toString 方法的實作很像呢?事實上,toString方法就是介面的一種變相實作。接口就是這樣,接口本身什麼都不做,系統悄悄地在內部實現了接口的行為,所以只要實現這個接口,就可以使用接口提供的方法。這就是介面「即插即用」思想
我們都知道,介面是多多重整合的一種變相實現,而在講繼承時,我們提到了用來實現混入(Minxin)式的Traits ,實際上,traits可以被視為一種加強版的介面。
來看下面的程式碼:
<?php trait Hello { public function sayHello() { echo 'Hello '; } } trait World { public function sayWorld() { echo 'Word'; } } class MyHelloWorld { use Hello, World; public function sayExclamationMark() { echo '!'; } } $o = new MyHelloWorld(); $o->sayHello(); $o->sayWorld(); $o->sayExclamationMark();
上面程式碼運行結果如下:
Hello Word!
這裡的MyHelloWorld同時實作了兩個traits,從而使其可以分別呼叫兩個Traits裡的代碼段。從程式碼就可以看出,Traits和介面很像,不同的是Traits是可以匯入包含程式碼的介面。從某種意義上來說,Traits和介面都是對「多重整合」的一種變相實作。
總結關於介面的幾個概念:
介面作為一種規範和契約存在。作為規範,介面應該保證可用性;作為契約介面應該保證可控性、
介面只是一個聲明,一旦使用interface關鍵字,就應該實現它。可以由程式設計師實現(外部介面),也可以由系統實現(內部介面)。介面本身什麼都不做,但是他可以高數我們它能做什麼。
PHP中的介面有兩個不足,一時沒有契約限制,二是缺少足夠的內部介面。
介面其實很簡單,但介面的各種應用很靈活,設計模式中也有很大一部分是圍繞著介面展開的。
以上是對php介面使用問題的一些總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!