Skip to content

Latest commit

 

History

History
224 lines (194 loc) · 6.34 KB

2.Interface.md

File metadata and controls

224 lines (194 loc) · 6.34 KB

无规矩不成方圆。你如果有一组类有类似的“行为”,需要对他们进行约束的话,接口就必不可少了。

只要定好规范,任第三份随意实现其代码。但是调用逻辑是不变的。

所以即使以后更换组件,只要其正确实现了所需接口,逻辑层代码都不需要变化。

# Example
class DocumentStore {
    protected $data = [];
    public function addDocument(Documentable $document) {
        $key = $document->getId();
        $value = $document->getContent();
        $this->data[$key] = $value;
    }
    public function getDocuments() {
        return $this->data; 
    }
}

interface Documentable {
    public function getId(); 
    public function getContent();
}

class HtmlDocument implements Documentable {
    protected $url;
    public function __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; 
    }
}

class StreamDocument implements Documentable {
    protected $resource; protected $buffer;
    public function __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; 
    }
}

class CommandOutputDocument implements Documentable {
    protected $command;
    public function __construct($command) {
        $this->command = $command;
    }
    public function getId() {
        return $this->command; 
    }
    public function getContent() {
        return shell_exec($this->command); 
    }
}

$documentStore = new DocumentStore();
// Add HTML document
$htmlDoc = new HtmlDocument('https://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());

5.4 新加入了 Traits特性,它既不是接口也不是类。主要是为了解决单继承语言的限制。与 Ruby 的 composable modulesmixins 类似。

它能被加入到一个或多个已经存在的类中。它声明了类能做什么(表明了其接口特性),同时也包含了具体实现(表明了其类特性)

Example:

<?php
trait Geocodable {
    /** @var string */
    protected $address;
    /** @var \Geocoder\Geocoder */
    protected $geocoder;
    /** @var \Geocoder\Result\Geocoded */
    protected $geocoderResult;
    public function setGeocoder(\Geocoder\GeocoderInterface $geocoder) {
        $this->geocoder = $geocoder;
    }
    public function setAddress($address) {
        $this->address = $address;
    }
    public function getLatitude(){
        if (isset($this->geocoderResult) === false) {
            $this->geocodeAddress();
        }
        return $this->geocoderResult->getLatitude(); 
    }
    public function getLongitude() {
        if (isset($this->geocoderResult) === false) { 
            $this->geocodeAddress();
        }
        return $this->geocoderResult->getLongitude(); 
    }
    protected function geocodeAddress() {
        $this->geocoderResult = $this->geocoder->geocode($this->address); 
        return true;
    } 
}
<?php
class RetailStore {
    use Geocodable;
    // Class implementation goes here
    // Now each RetailStore instance can use the properties and methods provided by the Geocodable trait
}
$geocoderAdapter = new \Geocoder\HttpAdapter\CurlHttpAdapter(); 
$geocoderProvider = new \Geocoder\Provider\GoogleMapsProvider($geocoderAdapter); 
$geocoder = new \Geocoder\Geocoder($geocoderProvider);
$store = new RetailStore();
$store->setAddress('420 9th Avenue, New York, NY 10001 USA'); 
$store->setGeocoder($geocoder);
$latitude = $store->getLatitude(); 
$longitude = $store->getLongitude(); 
echo $latitude, ':', $longitude;

PHP解释器将在编译时期将 Traits 的代码拷贝到类中。

从基类继承的成员被 traits 插入的成员所覆盖。

优先顺序是来自当前类的成员方法覆盖了 trait 的方法,而 trait 的方法则覆盖了被继承的方法。

如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。

为了解决多个 trait 在同一个类中的命名冲突,可以使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。

或者使用 as 操作符可以将其中一个冲突的方法以另一个名称来引入。

<?php
trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
        echo 'A';
    }
}

trait B {
    public function smallTalk() {
        echo 'b';
    }
    public function bigTalk() {
        echo 'B';
    }
}

class Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        B::bigTalk as talk;
    }
}
?>

使用 as 语法还可以用来调整方法的访问控制权限。

<?php
trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

// 修改 sayHello 的访问控制
class MyClass1 {
    use HelloWorld { sayHello as protected; }
}

// 给方法一个改变了访问控制的别名
// 原版 sayHello 的访问控制则没有发生变化
class MyClass2 {
    use HelloWorld { sayHello as private myPrivateHello; }
}
?>