代理模式:
为其他对象提供一种代理以控制对这个对象的访问。
代理模式是对象的结构模式,代理模式给某一个对象提供一个代理对象,并由此代理对象控制对原代理对象的引用。代理模式不应该让用户感觉到代理的存在,所以代理对象和原对象的对外的调用接口是一致的。
代理模式一般包括三个角色:
- 抽象主题角色(Subject):它的作用是统一接口。此角色定义了真实主题角色和代理主题角色共用的接口,这样就可以在使用真实主题角色的地方使用代理主题角色。
- 真实主题角色(RealSubject):隐藏在代理角色后面的真实对象。
- 代理主题角色(ProxySubject):它的作用是代理真实主题,在其内部保留了对真实主题角色的引用。它与真实主题角色都继承自抽象主题角色,保持接口的统一。它可以控制对真实主题的存取,并可能负责创建和删除真实对象。代理角色并不是简单的转发,通常在将调用传递给真实对象之前或之后执行某些操作,当然你也可以只是简单的转发。 与适配器模式相比:适配器模式是为了改变对象的接口,而代理模式并不能改变所代理对象的接口。
思维导图:
使用场景:实现延迟加载。
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(); // 直接从内存获取
通过反射来代理多个对象,这种方式有缺陷,看代码体会!具体使用还看应用场景
\r\n"; }}/** * 真实主题角色 B */final class RealSubjectB { public function __construct() { } public function actionB() { echo "actionB method in RealSubject B \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\r\n"; } /** * 请求后的操作 */ private function _afterAction() { echo "After action in ProxySubject\r\n"; }}$subject = new ProxySubject();$subject->addSubject(new RealSubjectA());$subject->addSubject(new RealSubjectB());$subject->actionA();$subject->actionB();