分类目录归档:PHP

PHP源码,PHP扩展,PHP程序

PHP设计模式笔记:使用PHP实现原型模式

PHP设计模式笔记:使用PHP实现原型模式

【意图】
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

【原型模式结构图】
Prototype

【原型模式中主要角色】
抽象原型(Prototype)角色:声明一个克隆自身的接口

具体原型(Concrete Prototype)角色:实现一个克隆自身的操作

【原型模式的优点和缺点】
Prototype模式优点:
1、可以在运行时刻增加和删除产品
2、可以改变值以指定新对象
3、可以改变结构以指定新对象
4、减少子类的构造
5、用类动态配置应用

Prototype模式的缺点:
Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。
而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。

【原型模式适用场景】
1、当一个系统应该独立于它的产品创建、构成和表示时,要使用Prototype模式
2、当要实例化的类是在运行时刻指定时,例如动态加载
3、为了避免创建一个与产品类层次平等的工厂类层次时;
4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些

【原型模式与其它模式】
抽象工厂模式(abstract factory模式):Abstract Factory模式与Prototype模式在某种方面是相互竞争的。但是也可以一起使用

【原型模式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
 
<?php
/**
 * 原型模式 2010-06-27 sz
 * @author phppan.p#gmail.com  http://www.phppan.com
 * 哥学社成员(http://www.blog-brother.com/)
 * @package design pattern
 */
 
/**
 * 抽象原型角色
 */
interface Prototype {
    public function copy();
}
 
/**
 * 具体原型角色
 */
class ConcretePrototype implements Prototype{
 
    private  $_name;
 
    public function __construct($name) {
        $this->_name = $name;
    }
 
    public function setName($name) {
        $this->_name = $name;
    }
 
    public function getName() {
        return $this->_name;
    }
 
    public function copy() {
       /* 深拷贝实现
        $serialize_obj = serialize($this);  //  序列化
        $clone_obj = unserialize($serialize_obj);   //  反序列化                                                     
        return $clone_obj;
        */
        return clone $this;     //  浅拷贝
    }
}
 
/**
 * 测试深拷贝用的引用类
 */
class Demo {
    public $array;
}
 
class Client {
 
     /**
     * Main program.
     */
    public static function main() {
 
        $demo = new Demo();
        $demo->array = array(1, 2);
        $object1 = new ConcretePrototype($demo);
        $object2 = $object1->copy();
 
        var_dump($object1->getName());
        echo '<br />';
        var_dump($object2->getName());
        echo '<br />';
 
        $demo->array = array(3, 4);
        var_dump($object1->getName());
        echo '<br />';
        var_dump($object2->getName());
        echo '<br />';
 
    }
 
}
 
Client::main();
?>

【浅拷贝与深拷贝】

浅拷贝
被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。
即 浅拷贝只负责当前对象实例,对引用的对象不做拷贝。

深拷贝
被拷贝对象的所有的变量都含有与原来对象相同的值,除了那些引用其他对象的变量。那些引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有那些被引用对象。
即 深拷贝把要拷贝的对象所引用的对象也都拷贝了一次,而这种对被引用到的对象拷贝叫做间接拷贝。
深拷贝要深入到多少层,是一个不确定的问题。
在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。
因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。

利用序列化来做深拷贝
利用序列化来做深拷贝,把对象写到流里的过程是序列化(Serilization)过程,但在业界又将串行化这一过程形象的称为“冷冻”或“腌咸菜”过程;
而把对象从流中读出来的过程则叫做反序列化(Deserialization)过程,也称为“解冻”或“回鲜”过程。
在PHP中使用serialize和unserialize函数实现序列化和反序列化

在上面的代码中的注释就是一个先序列化再反序列化实现深拷贝的过程

百度招聘的一道题

在烂叶的blog上看到一篇关于百度招聘的一道题
地址:百度招聘的一道试题

有些兴趣,自己实现了下,使用了类蒙地卡罗方法实现。

【题目】
2009百度实习笔试题
要求用PHP,shell或c完成
输入:N(整数)
输入:数据文件A.txt,不超过6条记录,字符串长度不超过15个字节
文件格式如下:
字符串\t数字\n

说明:
每行为1条记录;字符串中不含有\t。
数字描述的是该字符串的出现概率,小于等于100的整数。
多条记录的出现概率之和为100,如果A.txt不满足该条件,程序则退出;
如果文件格式错误,程序也退出。

要求:
编写一个程序,输入为N(正整数),读入文件A.txt,按照字符串出现概率随机地输出字符串,输出N条记录

例如:
输入文件A.txt
abc20
a30
de50
输入为:10

即 abc有20%的概率输出,a有30%的概率输出,de有50%的概率输出,输出10条记录
以下为一次输出的结果,多次输出的结果可能不相同。
abc
a
de
de
abc
de
a
de
a
de

【算法】
先将数据从文件中取出存放到数组,然后将概率空间分割为所给字符串个数个部分,然后生成N个1-100的随机数,然后看每个随机数落在哪一个区间,就将该区间对应的字符串输出。

【程序】

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
 
$filename = "A.txt";
 
/**
* 数据初始化 读取文件内容,拆分字符串形成数据,
* @param <type> $filename  数据文件名
* @return <type> 如果文件打开错误或文件错误返回FALSE                                                      
*/
function data_init($filename) {
    $lines = file($filename);
 
    if ($lines === FALSE) {
       return FALSE;
    }
 
    $data = array();
    foreach ($lines as $row) {
       $row = rtrim($row, "\r\n");
 
       $index = strpos($row, "\t");
       $key = substr($row, 0, $index);
 
       if (strlen($key) > 15) {
           return FALSE;
       }
 
       $data[$key] = (int) substr($row, $index + 1);
    }
 
    return $data;
}
 
$data = data_init($filename);
 
if ($data === FALSE) {  //  输入文件错误
    die('Input File Error!');
}
 
if (array_sum($data) != 100) {  //  输入数据错误
    die('Input Data Error!');
}
 
/* 以类蒙地卡罗方法实现 */
$map = array();
$k = 0;
 
/* 概率空间分割 */
foreach ($data as $key => $row) {                                                 
    $i = $row;
    for ($i = 1; $i <= $row; $i++) {
       $map[$k + $i] = $key;
    }
    $k += $row;
}
 
/* 随机输出 */
$count = 10;
for ($i = 0; $i < $count; $i++) {
    $rand_num = rand(1, 100);
    echo $map[$rand_num], '<br />';
}

关于蒙特卡洛方法请猛击:蒙特卡洛方法

PHP5.2.0之前版本使用JSON函数的方法

PHP5.2.0之前版本使用JSON函数的方法
我们知道PHP在其版本5.2.0之后才支持 JSON相关函数,在此版本之前如果我们要使用JSON函数则需要自己实现
JSON的算法已经有人实现了,其下载地址为:http://pear.php.net/pepr/pepr-proposal-show.php?id=198

但是它是一个类这与我们的使用习惯不同,于是我们需要将它包装下,其代码如下:

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
/**
* 对象转换成数组 递归函数
* @param <type> $value 需要转换的值                                
* @return <type>
*/
function object2array($value) {
   if (!(is_array($value) || is_object($value))) {
       return $value;
   }
 
   $array = array();
   foreach ($value as $key => $row) {
       $array[$key] = object2array($row);
   }
 
   return $array;
}
 
/**
* 替换JSON方法
*/
if (!function_exists("json_encode")) {
   include_once("JSON.php");
 
   function json_encode($array) {
       $json = new Services_JSON();
       $json_array = $json->encode($array);
       return $json_array;
   }
 
   /**
    * 解析JSON数据
    * @param string $json_data 需要解析的JSON数据                                                      
    * @param bool $toarray 是否需要解析成数组                                      
    * @return array 返回解析后的数组
    */
   function json_decode($json_data, $toarray = TRUE) {
       $json = new Services_JSON();
       $array = $json->decode($json_data);
 
       if ($toarray) { //  需要转换成数组
           $array = object2array($array);
       }
       return $array;
   }
 
}

不过以上的递归实现在效率上有一些慢,它需要遍历这个数组的所有元素,
如果只限于实现二维数组,可以考虑直接使用循环+类型强制转化来做
【EOF】