标签归档:设计模式

代理模式(Proxy)和PHP的反射功能

代理模式(Proxy)和PHP的反射功能

本文包括以下内容:

  • 代理模式概述
  • 代理模式常规示例
  • 使用PHP的反射功能实现多代理

正文

模式意图 :为其他对象提供一种代理以控制对这个对象的访问[GOF95]

代理模式是对象的结构模式,代理模式给某一个对象提供一个代理对象,并由此代理对象控制对原代理对象的引用。代理模式不应该让用户感觉到代理的存在,所以代理对象和原对象的对外的调用接口是一致的。

代理模式一般包括三个角色:

  • 抽象主题角色(Subject):它的作用是统一接口。此角色定义了真实主题角色和代理主题角色共用的接口,这样就可以在使用真实主题角色的地方使用代理主题角色。
  • 真实主题角色(RealSubject):隐藏在代理角色后面的真实对象。
  • 代理主题角色(ProxySubject):它的作用是代理真实主题,在其内部保留了对真实主题角色的引用。它与真实主题角色都继承自抽象主题角色,保持接口的统一。它可以控制对真实主题的存取,并可能负责创建和删除真实对象。代理角色并不是简单的转发,通常在将调用传递给真实对象之前或之后执行某些操作,当然你也可以只是简单的转发。 与适配器模式相比:适配器模式是为了改变对象的接口,而代理模式并不能改变所代理对象的接口。

从以上三个角色我们可以得出一个简单的示例:

/**
 * 代理模式简单示例 2011-10-30 sz
 * @author phppan.p#gmail.com  http://www.phppan.com
 * @package design pattern
 */

/**
 * 抽象主题角色
 */
abstract class Subject {
    abstract public function action();
}

/**
 * 真实主题角色
 */
class RealSubject extends Subject {

    public function __construct() {
    }

    public function action() {
        echo "action method in RealSubject<br />\r\n";
    }

}

/**
 * 代理主题角色
 */
class ProxySubject extends Subject {

    private $_real_subject = NULL;

    public function __construct() {
    }

    public function action() {
        $this->_beforeAction();

        if (is_null($this->_real_subject)) {
            $this->_real_subject = new RealSubject();
        }

        $this->_real_subject->action();

        $this->_afterAction();
    }

    /**
     * 请求前的操作
     */
    private function _beforeAction() {
        echo "Before action in ProxySubject<br />\r\n";
    }

    /**
     * 请求后的操作
     */
    private function _afterAction() {
        echo "After action in ProxySubject<br />\r\n";
    }

}

/**
 * 客户端
 */
class Client {

    public static function main() {
        $subject = new ProxySubject();
        $subject->action();
    }
}

Client::main();
?>

以上的示例适用于代理模式为一个对象提供一个代理对象。当为多个对象提供代理对象时,是否我们需要创建多个代理对象呢? 如此按这种实现确实是,但是我们也可以将一个代理对象给多个对象用,或者可以作为一个代理工厂,此时一个对象会保留多个对象的引用, 并且在执行操作时需要判断所代理的是哪个对象,那么此时这种判断如何进行呢? 以多个instanceof判断对象的归属?但是这样,代理对象也必须知道真实对象的方法, 如果我们现在的需求是实现一个代理对象和真实对象的松耦合,但是在代理对象中,对于每个真实对象都有一个前置操作和后转操作, 也许我们可以使用反射,那么反射是什么呢?

反射一般来说是指在程序执行过程中获取程序相关的信息或者修改程序信息。 如获取类、方法、函数等的详细信息,或删除类方法定义。ruby的反射功能实现了类、对象、常量、变更等的获取和修改,但是在PHP中只有获取功能而无修改。 在PHP中我们可以使用PHP5以后的Reflection扩展, 此扩展是PHP的核心扩展,在PHP安装时就已经自动加载,它的作用是分析PHP程序, 导出或提取出关于类、方法、函数、属性、参数等的详细信息,包括注释。 在这里我们经常可以此扩展来实现对PHP程序内部关于类、方法等的信息检测,并做作出处理。

<?PHP
/**
 * 使用反射实现代理工厂 2011-10-30 sz
 * @author phppan.p#gmail.com  http://www.phppan.com
 * @package design pattern
 */

/**
 * 真实主题角色 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";
    }

}

/**
 * 客户端
 */
class Client {

    public static function main() {
        $subject = new ProxySubject();

        $subject->addSubject(new RealSubjectA());
        $subject->addSubject(new RealSubjectB());

        $subject->actionA();
        $subject->actionB();
    }

}

Client::main();
?>

以上示例使用反射机制实现了对不同真实对象的代理,但是还存在一些问题:

  • 对象的显式添加,不能对用户透明
  • 真实对象的重复问题,如果存在多个相同的对象添加到对象列表中,如何处理?Flyweigh?

面向模式的软件架构–分布式计算的模式语言读后感

数个早晨,捧着这本书,一个人

终于看完了,在痛苦中煎熬,回头望去,却不知所得。只看过GOF的23个模式及其它一些书籍中不成系统的介绍。 仰望这一望无际的模式,作者对模式信手拈来,娓娓而谈,不得不叹服。至此,不敢写总结,只能写读后感,权当慰藉。

每种模式是记录了一个在特定环境下不断重复发生的问题以及相应的解决方案。 也许一种实现方法很常见,或者说已经用过,这是个人经验。 模式与个人经验相比:它被正式命名并记录,更有利于提炼、交流和分享架构层次上的知识。 本书一共介绍了114种模式,并且连接到其它文献中的介绍的150多种模式。 如此多的模式浓缩到这一本书中,确实页页都是精华。 读完整本书,以前的一些模糊性的内容被整理成模式语言的论述,如Layers、Pipes and Filters等等。 这些都有见过或用过,却不成系统。 由于接触分布式的内容较少,对于作者在分布式基础设施中提出的模式很难理解,待后续。 对于GOF的23种模式,作者按其主题分到了不同的章,每当遇到这些会有一种熟悉感,从而对其它模式的理解有一定的帮助作用。 但是读完本书后,觉得这23种模式处于一个基础或者说是实现层次位置。

每章,作者列出主题和此主题中的问题,然后展示可以解决这些问题的模式,以及相关模式的关系。 对每个模式,作者列出此模式的相关模式,此模式的应用场景,此模式的意图和大概实现。

对于这本书,我是直接从头到尾的通读,遇到不懂的模式去书上或网上查找,不懂的跳过,通读而已。 其它我们可以按主题阅读(按章阅读)或按模式阅读(按小节阅读)。 不过如果对模式了解不多,如我,还是老老实实从头到尾先看一遍吧,后面读第二遍再按主题和模式阅读吧。

虽然有人反对模式,个人认为,存在即合理。模式将一些经验性的内容记录下来,值得推崇,却不可全按此来, 所有的解决方案都是基于特定问题的处理,虽然有通用的方案,却只能解决一些常见的问题,当我们遇到问题时,模式是一个很好的参考方案。 尽信书不如无书,若书未阅,何来信书之说?

现在,好读书,不求甚解,每有会意,欣然却不敢忘食。

PHP设计模式笔记:使用PHP实现备忘录模式

PHP设计模式笔记:使用PHP实现备忘录模式

【意图】
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样可以在以后把该对象的状态恢复到之前保存的状态。【GOF95】

【备忘录模式结构图】

备忘录模式

备忘录模式

【备忘录模式中主要角色】
1、备忘录(Memento)角色:
存储发起人(Originator)对象的内部状态,而发起人根据需要决定备忘录存储发起人的哪些内部状态。
备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。

2、发起人(Originator)角色:
创建一个含有当前的内部状态的备忘录对象
使用备忘录对象存储其内部状态

3、负责人(Caretaker)角色:
负责保存备忘录对象,不检查备忘录对象的内容

【备忘录模式的优点和缺点】
备忘录模式的优点:
1、有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取。
2、简化了发起人(Originator)类。发起人(Originator)不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理它们所需要的这些状态的版本
3、当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。

备忘录模式的缺点:
1、如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。
2、当负责人角色将一个备忘录存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否会很昂贵。
3、当发起人角色的状态改变的时候,有可能这个状态无效。

【备忘录模式适用场景】
1、必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
2、如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

【备忘录模式与其它模式】
1、命令模式(command模式):Command模式也可以用来恢复对象的状态,一般Command模式可以支持多级状态的回滚,Memento只是简单的恢复(快照)。在Command模式的每一个undo中,可以使用Memento来保存对象的状态。
2、迭代器模式(Iterator模式):备忘录可以用于迭代

【备忘录模式PHP示例】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
 
<?php
 
/**
 * 备忘录模式 2010-10-09 sz
 * @author phppan.p#gmail.com  http://www.phppan.com                                                       
 * 哥学社成员(http://www.blog-brother.com/)
 * @package design pattern
 */
 
/**
 * 发起人(Originator)角色
 */
class Originator {
 
    private $_state;
 
    public function __construct() {
        $this->_state = '';
    }
 
    /**
     * 创建备忘录
     * @return Memento 包含当前状态的备忘录对象
     */
    public function createMemento() {
        return new Memento($this->_state);
    }
 
    /**
     * 将发起人恢复到备忘录对象记录的状态上
     * @param Memento $memento
     */
    public function restoreMemento(Memento $memento) {
        $this->_state = $memento->getState();
    }
 
    public function setState($state) {
        $this->_state = $state;
    }
 
    public function getState() {
        return $this->_state;
    }
 
    /**
     * 测试用方法,显示状态
     */
    public function showState() {
        echo "Original Status:", $this->getState(), "<br />";
    }
 
}
 
/**
 * 备忘录(Memento)角色
 */
class Memento {
 
    private $_state;
 
    public function __construct($state) {
        $this->setState($state);
    }
 
    public function getState() {
        return $this->_state;
    }
 
    public function setState($state) {
        $this->_state = $state;
    }
 
}
 
/**
 * 负责人(Caretaker)角色
 */
class Caretaker {
 
    private $_memento;
 
    public function getMemento() {
        return $this->_memento;
    }
 
    public function setMemento(Memento $memento) {
        $this->_memento = $memento;
    }
 
}
 
/**
 * 客户端
 */
class Client {
 
    /**
     * Main program.
     */
    public static function main() {
 
        /* 创建目标对象 */
        $org = new Originator();
        $org->setState('open');
        $org->showState();
 
        /* 创建备忘 */
        $memento = $org->createMemento();
 
        /* 通过Caretaker保存此备忘 */
        $caretaker = new Caretaker();
        $caretaker->setMemento($memento);
 
        /* 改变目标对象的状态 */
        $org->setState('close');
        $org->showState();
 
        /* 还原操作 */
        $org->restoreMemento($caretaker->getMemento());
        $org->showState();
    }
 
}
 
Client::main();
?>