代理模式
代理模式:
为其他对象提供一种代理以控制对这个对象的访问。
代理模式是对象的结构模式,代理模式给某一个对象提供一个代理对象,并由此代理对象控制对原代理对象的引用。代理模式不应该让用户感觉到代理的存在,所以代理对象和原对象的对外的调用接口是一致的。
代理模式一般包括三个角色:
- 抽象主题角色(Subject):它的作用是统一接口。此角色定义了真实主题角色和代理主题角色共用的接口,这样就可以在使用真实主题角色的地方使用代理主题角色。
- 真实主题角色(RealSubject):隐藏在代理角色后面的真实对象。
- 代理主题角色(ProxySubject):它的作用是代理真实主题,在其内部保留了对真实主题角色的引用。它与真实主题角色都继承自抽象主题角色,保持接口的统一。它可以控制对真实主题的存取,并可能负责创建和删除真实对象。代理角色并不是简单的转发,通常在将调用传递给真实对象之前或之后执行某些操作,当然你也可以只是简单的转发。 与适配器模式相比:适配器模式是为了改变对象的接口,而代理模式并不能改变所代理对象的接口。
思维导图:
使用场景:实现延迟加载。
<?php/** * Proxy design pattern (lazy loading)@global * 实现了图片的延迟加载 */ /* 抽象主题角色,提供统一接口 */ interface ImageInterface { public function display(); } /* 真是主题角色,就是需要被代理的类 */ class Image implements ImageInterface { protected $filename; public function __construct($filename) { $this->filename = $filename; $this->loadFromDisk(); } protected function loadFromDisk() { echo "Loading {$this->filename}\n"; } public function display() { echo "Display {$this->filename}\n"; } } /* 代理主题角色,原图片类在实例化时就把图片加载到内存了,用代理修改为display时才执行加载 */ class ProxyImage implements ImageInterface { protected $id; protected $image; protected $filename; public function __construct($filename) { $this->filename = $filename; } public function display() { if (null === $this->image) { //缓存 $this->image = new Image($this->filename); } return $this->image->display(); } } //调用Image类,在实例化时就加载到内存了,如果没调display就浪费了 $filename = 'test.png'; $image1 = new Image($filename); // 已经加载到内存 echo $image1->display(); //代理类是在display时才把图片从磁盘加入内存 $image2 = new ProxyImage($filename); echo $image2->display(); // 此时才加载到内存 echo $image2->display(); // 直接从内存获取
通过反射来代理多个对象,这种方式有缺陷,看代码体会!具体使用还看应用场景
<?PHP /** * 使用反射实现代理工厂 */ /** * 真实主题角色 A */ final class RealSubjectA { public function __construct() { } public function actionA() { echo "actionA method in RealSubject A <br />\r\n"; } } /** * 真实主题角色 B */ final class RealSubjectB { public function __construct() { } public function actionB() { echo "actionB method in RealSubject B <br />\r\n"; } } /** * 代理主题角色 */ final class ProxySubject { private $_real_subjects = NULL; public function __construct() { $this->_real_subjects = array(); } /** * 动态添加真实主题 * @param type $subject */ public function addSubject($subject) { $this->_real_subjects[] = $subject; } public function __call($name, $args) { foreach ($this->_real_subjects as $real_subject) { /* 使用反射获取类及方法相关信息 */ $reflection = new ReflectionClass($real_subject); /* 如果不存在此方法,下一元素 */ if (!$reflection->hasMethod($name)) { continue; } $method = $reflection->getMethod($name); /* 判断方法是否为公用方法并且是否不为抽象方法 */ if ($method && $method->isPublic() && !$method->isAbstract()) { $this->_beforeAction(); $method->invoke($real_subject, $args); $this->_afterAction(); break; } } } /** * 请求前的操作 */ private function _beforeAction() { echo "Before action in ProxySubject<br />\r\n"; } /** * 请求后的操作 */ private function _afterAction() { echo "After action in ProxySubject<br />\r\n"; } } $subject = new ProxySubject(); $subject->addSubject(new RealSubjectA()); $subject->addSubject(new RealSubjectB()); $subject->actionA(); $subject->actionB();