ServiceLocator类继承了Component类和Object类的所有特性,也就是说他现在拥有了属性,时间和行为这三个武器。服务定位器是一种设计模式,使用它的目的就是解耦,使得服务请求方不需要直接对服务提供者进行操作,只需要告诉服务定位器我现在想要什么服务,服务定位器就能够定位到能够提供这个服务的组件。
从直观的角度理解,假如现在我想发送一个邮件,如果没有采用服务定位器这样的设计方法,那我现在肯定需要在现在的代码里面,将发送邮件的这个组件进行实例化,然后进行发送邮件的操作。但是有时候突然发现这个发送邮件的组件不好用,需要换一个,那就必须要修改我的代码了,这样一来二去的就很烦人,因为对于客户端来说,他只是仅仅想发送一个邮件,提供服务的组件不好用了竟然还得需要让他来修改代码。
服务定位器的出现打破了这个状况,我现在只要告诉服务定位器我想要发送一个邮件,那么服务定位器就能提供我需要的组件,我拿过来直接用就可以了,如果这个组件想换了,只需要在服务定位器这里将发送邮件这个服务定位到其他的某一个发送邮件的组件就可以了,客户端的代码根本不需要改变。
现在就来分析一下服务定位器这个模式需要什么要素。想来想去还真的好简单,不就是一个数组就行了嘛!key为组件名称,value为提供组件的类。接下来就看看在Yii框架中这个类是怎么实现的吧!
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函数用来实现components属性,也就是说,ServiceLocator的配置数组里面有一个key为components,并且这个key也对应了一个数组,数组的每一项是key=>value类型的,key为组件名称,value包含了提供组件的类。
ServiceLocator中有两个数组来存储组件,一个是$_definitions,用于直接存储配置数组里面的内容,里面有可能仅仅存储了提供组件的类的名称。一个是$_components,里面是提供组件的类的实例。
两个函数get()和set()就是用来获取服务和设置服务的,设置服务的时候一开始并没有将提供服务的类实例化,只有当获取到某一个服务的时候,如果提供这个服务的类没有被实例化,他才会被实例化,get()函数会返回这个对象。