ServiceLocator類繼承了Component類和Object類的所有特性溜畅,也就是說他現(xiàn)在擁有了屬性床估,時間和行為這三個武器荐捻。服務定位器是一種設計模式,使用它的目的就是解耦糠惫,使得服務請求方不需要直接對服務提供者進行操作,只需要告訴服務定位器我現(xiàn)在想要什么服務钉疫,服務定位器就能夠定位到能夠提供這個服務的組件硼讽。
從直觀的角度理解,假如現(xiàn)在我想發(fā)送一個郵件牲阁,如果沒有采用服務定位器這樣的設計方法固阁,那我現(xiàn)在肯定需要在現(xiàn)在的代碼里面,將發(fā)送郵件的這個組件進行實例化城菊,然后進行發(fā)送郵件的操作备燃。但是有時候突然發(fā)現(xiàn)這個發(fā)送郵件的組件不好用,需要換一個凌唬,那就必須要修改我的代碼了并齐,這樣一來二去的就很煩人,因為對于客戶端來說客税,他只是僅僅想發(fā)送一個郵件况褪,提供服務的組件不好用了竟然還得需要讓他來修改代碼。
服務定位器的出現(xiàn)打破了這個狀況更耻,我現(xiàn)在只要告訴服務定位器我想要發(fā)送一個郵件测垛,那么服務定位器就能提供我需要的組件,我拿過來直接用就可以了秧均,如果這個組件想換了食侮,只需要在服務定位器這里將發(fā)送郵件這個服務定位到其他的某一個發(fā)送郵件的組件就可以了,客戶端的代碼根本不需要改變目胡。
現(xiàn)在就來分析一下服務定位器這個模式需要什么要素锯七。想來想去還真的好簡單,不就是一個數(shù)組就行了嘛誉己!key為組件名稱眉尸,value為提供組件的類。接下來就看看在Yii框架中這個類是怎么實現(xiàn)的吧巫延!
class ServiceLocator extends Component
{
private $_components = [];
private $_definitions = [];
public function __get($name)
{
if ($this->has($name)) {
return $this->get($name);
} else {
return parent::__get($name);
}
}
public function __isset($name)
{
if ($this->has($name, true)) {
return true;
} else {
return parent::__isset($name);
}
}
public function has($id, $checkInstance = false)
{
return $checkInstance ? isset($this->_components[$id]) : isset($this->_definitions[$id]);
}
public function get($id, $throwException = true)
{
if (isset($this->_components[$id])) {
return $this->_components[$id];
}
if (isset($this->_definitions[$id])) {
$definition = $this->_definitions[$id];
if (is_object($definition) && !$definition instanceof Closure) {
return $this->_components[$id] = $definition;
} else {
return $this->_components[$id] = Yii::createObject($definition);
}
} elseif ($throwException) {
throw new InvalidConfigException("Unknown component ID: $id");
} else {
return null;
}
}
public function set($id, $definition)
{
if ($definition === null) {
unset($this->_components[$id], $this->_definitions[$id]);
return;
}
unset($this->_components[$id]);
if (is_object($definition) || is_callable($definition, true)) {
// an object, a class name, or a PHP callable
$this->_definitions[$id] = $definition;
} elseif (is_array($definition)) {
// a configuration array
if (isset($definition['class'])) {
$this->_definitions[$id] = $definition;
} else {
throw
}
} else {
throw
}
}
public function clear($id)
{
unset($this->_definitions[$id], $this->_components[$id]);
}
public function getComponents($returnDefinitions = true)
{
return $returnDefinitions ? $this->_definitions : $this->_components;
}
public function setComponents($components)
{
foreach ($components as $id => $component) {
$this->set($id, $component);
}
}
}
看著好簡單效五,最后的兩個set和get函數(shù)用來實現(xiàn)components屬性地消,也就是說炉峰,ServiceLocator的配置數(shù)組里面有一個key為components,并且這個key也對應了一個數(shù)組脉执,數(shù)組的每一項是key=>value類型的疼阔,key為組件名稱,value包含了提供組件的類。
ServiceLocator中有兩個數(shù)組來存儲組件婆廊,一個是$_definitions迅细,用于直接存儲配置數(shù)組里面的內容,里面有可能僅僅存儲了提供組件的類的名稱淘邻。一個是$_components茵典,里面是提供組件的類的實例。
兩個函數(shù)get()和set()就是用來獲取服務和設置服務的宾舅,設置服務的時候一開始并沒有將提供服務的類實例化统阿,只有當獲取到某一個服務的時候,如果提供這個服務的類沒有被實例化筹我,他才會被實例化扶平,get()函數(shù)會返回這個對象。