首先!
接口也可以繼承,通過使用 extends 操作符。
案例:
<?php
interface a
{
public function foo();
}
interface b extends a
{
public function baz(Baz $baz);
}
// 正确寫法
class c implements b
{
public function foo()
{
}
public function baz(Baz $baz)
{
}
}
然後!
我們在來說說我們的主題!
接口不是新特性但是很重要,接口是兩個php對象的契約。其目的不是讓一個對象依賴另一個對象的身份,而是依賴另一個對象的能力。接口把我們的代碼和依賴解耦,而且允許我們的代碼依賴任何實作了預期接口的第三方代碼。我們不關心第三方代碼如何實作接口,隻去關心他有沒有去實作接口。
如果我們寫的類去處理特定的對象, 那麼類的功能就被限定了,隻能處理那個類。但是我們的對象如果是處理的接口,那麼代碼立即就能知道如何處理實作這一接口的任何對象,我們的代碼不管接口如何實作隻需要關心有沒有實作。
文檔處理類實作
<?php
class DocumentStore{
protected $data = [];
/**
* 參數限定為 Documentable 對象,這是一個接口
*/
public function addDocument(Documentable $document){
$key = $document->getId();
$value = $document->getContent();
$this->data[$key] = $value;
}
public function getDocuments(){
return $this->data;
}
}
這個是我們的文檔處理類,它面向的是接口操作 Documentable 實作:
<?php
interface Documentable{
public function getId();
public function getContent();
}
具體實作接口的類,比如是從html獲得的文檔 實作:
<?php
class HtmlDocument implements Documentable{
protected $url;
publicfunction__construct($url)
{
$this->url = $url;
}
public function getId()
{
return $this->url;
}
public function getContent()
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
$html = curl_exec($ch);
curl_close($ch);
return $html;
}
}
讀取流資料的文檔 實作:
<?php
class StreamDocument implements Documentable{
protected $resource;
protected $buffer;
publicfunction__construct($resource, $buffer = 4096)
{
$this->resource = $resource;
$this->buffer = $buffer;
}
public function getId()
{
return 'resource-' . (int)$this->resource;
}
public function getContent()
{
$streamContent = '';
rewind($this->resource);
while(feof($this->resource) === false) {
$streamContent .= fread($this->resource, $this->buffer);
}
return $streamContent;
}
}
具體使用 :
<?php
require 'Documentable.php';
require 'DocumentStore.php';
require 'HtmlDocument.php';
require 'StreamDocument.php';
require 'CommandOutputDocument.php';
$documentStore = newDocumentStore();
// Add HTML document
$htmlDoc = new HtmlDocument('http://php.net');
$documentStore->addDocument($htmlDoc);
// Add stream document
$streamDoc = new StreamDocument(fopen('stream.txt', 'rb'));
$documentStore->addDocument($streamDoc);
// Add terminal command document
$cmdDoc = new CommandOutputDocument('cat /etc/hosts');
$documentStore->addDocument($cmdDoc);
print_r($documentStore->getDocuments());
需要說明的是參數類型 addDocument 參數類型限定為 Documentable 實作該接口的對象都可以做參數。
性狀( trait )
性狀是類的部分實作,可以混入一個或者多個現有的類實作中,有兩個作用:
1表明類可以做什麼;
2 提供子產品化實作;
使用場景:
我們做面向對象的開發的時候都會通過基類實作基本功能,完後子類具體實作詳細的功能,各類之間有明顯的自然的繼承關系,如果有一個邏輯既不屬于A類也不屬于B類,那麼在性狀出現之前我們怎麼解決:
解決辦法一:做一個父類 讓A, B都繼承,這樣做的缺點是,強制把兩個不相關的類繼承同一父類,結構混亂破壞了封裝。
解決方法二:做一個接口,讓A, B都去實作這個接口,強于上一個方法,但是缺點是相同的邏輯會在多個類中實作,代碼邏輯備援,加大維護成本。
解決辦法三:使用性狀(trait)推薦做法。
定義性狀:
<?php
// 推薦把性狀當類看待,一個檔案定義一個性狀
trait MyTrait {
protected $p1;
public $p2;
public function f1(){
}
}
使用性狀:
<?php
class MyClass{
use MyTrait;
}