《PHP设计模式介绍》第七章 策略模式(2)_PHP教程
推荐:《PHP设计模式介绍》第六章 伪对象模式面向对象的编程之所以丰富多彩,部分是由于对象间的相互联系与作用。一个单一的对象就能封装一个复杂的子系统,使那些很复杂的操作能够通过一些方法的调用而简化。(无所不在的数据库连接就是这
把一下这段代码加入到测试环境中以后,你可以确保每一个测试过程从开始的时候就各自独立地运行:
The Strategy Pattern 127 class VarCacheTestCase extends UnitTestCase { function setup() { @unlink(‘cache/foo.php’); } // ... } |
现在缓存的文件在每一次测试执行之前都没删除掉了,这保证了每一次测试运行都是相互独立的。(测试驱动的开发更实用的方法是你可以写一个VarCache::clear()方式函数去处理一个缓存的参数的清除工作。)
当上一次代码执行出来的缓存结果被清除了,测试重新开始运行,这意味着你可以继续测试并编写新的代码。
class VarCacheTestCase extends UnitTestCase { function setup() { /* ... */ } function TestUnsetValueIsInvalid() { /* ... */ } function TestIsValidTrueAfterSet() { /* ... */ } function TestCacheRetainsValue() { $test_val = ‘test’.rand(1,100); $cache =& new VarCache(‘foo’); $cache->set($test_val); $this->assertEqual($test_val, $cache->get()); } |
上面的测试验证VarCache::get()返回的值是否与用VarCache::set()设置的相同。
class VarCache { |
通过黑体字部分的代码,VarCache::set() 方式函数把参数$value的内容写到文件中,并用VarCache::get() 方式函数通过file_get_content() 从文件中把内容读取出来.
从目前的执行情况来看,对于字符串和数字的操作是没有问题的,但是对于更复杂的参数比如数组和对象,执行起来就会出现问题了。我们用下面的代码进行处理:
class VarCacheTestCase extends UnitTestCase { // ... function TestStringFailsForArray() { $test_val = array(‘one’,’two’); $cache =& new VarCache(‘foo’); $cache->set($test_val); $this->assertError(‘Array to string conversion’); $this->assertNotEqual($test_val, $cache->get()); $this->assertEqual(‘array’,strtolower($cache->get())); } |
由于篇幅的关系,我们直接调到这个执行过程的结束部分,它随后也将实现策略式的判断。
这里就是增加一系列操作用来完善VarCache的地方。
class VarCache { //... function get() { if ($this->isValid()) { include $this->_name.’.php’; return $cached_content; } //... } |
在这里关键性的改变是get() 方式函数(并且让PHP去验证有效性。
同时,get()返回参数$cached_content的值,所以无论set() 如果操作,它必须设置这个变量!
因此,对于数字来说,执行出来是什么样的结果呢?
class VarCache { //... function set($value) { $file_handle = fopen($this->_name.’.php’, ‘w’); $template = ‘<?php $cached_content = %s;’; $content = sprintf($template The Strategy Pattern 129 ,(float)$value); fwrite($file_handle, $content); fclose($file_handle); } } |
看起来对于一个数字,执行起来是没有问题的,那么对于字符串如何呢?对于字符串,缓存文件的数据编写方式就必须用= ‘%s’;结尾而不是= %s;。所以在这里我们需要引入一个“type” 参数:它用来指定缓存的数据类型是一个整型还是字符串。为了更容易地增加更多的数据类型,我们分别在set()和_getTemplate()函数增加一个case 判断。
class VarCache { var $_name; var $_type; function VarCache($name, $type=’string’) { $this->_name = ‘cache/’.$name; $this->_type = $type; } // ... function _getTemplate() { $template = ‘<?php $cached_content = ‘; switch ($this->_type) { case ‘string’: $template .= “‘%s’;”; break; case ‘numeric’: $template .= ‘%s;’; break; default: trigger_error(‘invalid cache type’); } return $template; } function set($value) { $file_handle = fopen($this->_name.’.php’, ‘w’); switch ($this->_type) { case ‘string’: $content = sprintf($this->_getTemplate() ,str_replace(“‘“,”\\’”,$value)); break; case ‘numeric’: $content = sprintf($this->_getTemplate() ,(float)$value); break; default: trigger_error(‘invalid cache type’); } fwrite($file_handle, $content); fclose($file_handle); } } |
现在,构造函数增加了第二个可选的参数用来确定第一个参数的数据类型是数字类型还是字符串。这个类的最终形式变为请看下面代码,包括了一个‘serialize’ 用来存储数据、对象等复杂数据的存储类型。
class VarCache { var $_name; var $_type; function VarCache($name, $type=’serialize’) { $this->_name = ‘cache/’.$name; $this->_type = $type; } function isValid() { return file_exists($this->_name.’.php’); } function get() { if ($this->isValid()) { include $this->_name.’.php’ return $cached_content; } } function _getTemplate() { $template = ‘<?php $cached_content = ‘; switch ($this->_type) { case ‘string’: $template .= “‘%s’;”; break; case ‘serialize’: $template .= “unserialize(stripslashes(‘%s’));”; break; case ‘numeric’: $template .= ‘%s;’; break; default: trigger_error(‘invalid cache type’); } return $template; } function set($value) { $file_handle = fopen($this->_name.’.php’, ‘w’); switch ($this->_type) { case ‘string’: $content = sprintf($this->_getTemplate() ,str_replace(“‘“,”\\’”,$value)); break; case ‘serialize’: $content = sprintf($this->_getTemplate() ,addslashes(serialize($value))); break; case ‘numeric’: $content = sprintf($this->_getTemplate() ,(float)$value); break; default: trigger_error(‘invalid cache type’); } fwrite($file_handle, $content); The Strategy Pattern 131 fclose($file_handle); } } |
请注意_getTemplate()和set() 函数中的case判断语句。它们都是基于同一个$_type 实例参数的。get() 函数中却没有受到$_type的影响,所以看起来因为存储的数据类型的变化只影响到数据的存储过程。同时,多重的case条件判断也是一个提示,这个地方如果使用了策略的设计模式会更好。
分享:《PHP设计模式介绍》第五章 注册模式我们通常认为避免使用全局变量是一种好的选择,因此,对象经常被作为参数从一段代码传递到另一段。但是传递实例的一个问题就是对象有时候不知道将要传递给谁——?经过一个函数后才被传
- 相关链接:
- 教程说明:
PHP教程-《PHP设计模式介绍》第七章 策略模式(2)。