样本代码
从一个多重的switch 条件判断改变到策略模式是一个条件分解实例的经典例子。整个测试的环境没有变化;只是VarCache类的内部改变了。
首先我们把你想要封装在一个独立的类的各种情况分隔出来。就前面的例子来说,你有三种变化的情况需要进行考虑:‘string’, ‘numeric’, 和第三个‘serialize’。前面的例子中还在对象实例化的时候选择了数据输出的格式。基于这个运算法则,你需要创建一个API来封装它。
你可以用以下的代码开始:
class CacheWriter { function store($file_handle, $var) { die(‘abstract class-implement in concrete CacheWriter’); } } |
这个就是PHP4版本的接口。(你可以从这个类进行继承来保证你使用的是子类,这样做的话只是增加了一些系统的负载。尤其在基类CacheWriter是在另外一个文件定义的时候。负载增加得稍微多一些。)
基类CacheWriter 调用了store() 方式函数来引入文件处理资源和参数来进行存储。每一个实际的类都从执行store()函数, 但是不同的实例在store()函数里面使用的运算法则是不一样的,以便不同的数据类型生成的$cached_content是不同的。每一个运算法则被当作一个单独的类来运行。
前面的例子中的代码被替换为:
class VarCache { |
set() 方式函数到相对应的类当中。这里是StringCacheWriter:
class StringCacheWriter /* implements CacheWriter */ { function store($file_handle, $string) { $content = sprintf( “<?php\n\$cached_content = ‘%s’;” ,str_replace(“‘“,”\\’”,$string)); fwrite($file_handle, $contents); } } |
(因为PHP 4不支持接口的使用,这里接口只是用注释来简单描述一下。)
这里我们得到另外一个运算法则存储“策略”。
class NumericCacheWriter /* implements CacheWriter */ { function store($file_handle, $numeric) { $content = sprintf(“<?php\n\$cached_content = %s;” ,(double)$numeric); The Strategy Pattern 133 fwrite($file_handle, $content); } } class SerializingCacheWriter /* implements CacheWriter */ { function store($file_handle, $var) { $content = sprintf( “<?php\n\$cached_content = unserialize(stripslashes(‘%s’));” ,addslashes(serialize($var))); fwrite($file_handle, $content); } } |
通过把运算法则封装到交互的类中(同样的API,多形性),你现在可以回过头来通过策略设计模式重新执行VarCache()类。这个时候经过条件分解但是与原来非常类似的代码可以继续运行了。
class VarCache { } function isValid() { return file_exists($this->_name.’.php’); } function get() { if ($this->isValid()) { include $this->_name.’.php’; return $cached_content; } } function set($value) { $file_handle = fopen($this->_name.’.php’, ‘w’); $this->_type->store($file_handle, $value); fclose($file_handle); } } |
缓存文件的时候,我们将不再关心初始化的时候是用什么运算法则来存储数据。
下面描述了定义策略设计模式的几个特性:一系列的运算法则,每个运算法则都是封装在独立的类中。但是,每一个对象都是绑定到一个公共的容器对象中。并且,通过一个公共的API使用同样的方式在进行引用。而这个公共的API的运行方式是与策略的选择无关的。
评论
策略设计模式的功能是非常强大的 。本书到现在为止所说的其它的设计模式提供的都是应用的基础模块功能,而 策略设计模式是目前第一个拥有设计模式和项目的迁移里面关键功能的设计模式。
它可以替换掉一个对象里面编写复杂的部分,改变整个对象的运行和性能,这点功能是非常强大的。另外,一个特定策略使用以后马上就被清空了,这个使得剩下的API非常容易执行。从根本上说,选用哪个运算法则对于其它的代码来说都是透明的。
互联网上有这么一个说法“本质上说,任何一个事情开始的时候都像在使用策略模式。”为什么呢?因为这个设计模式有效应用了多形性的特点,而这个也是面向对象编程最强大的几个方面之一。
相关的设计模式
策略模式和其它许多设计模式比较起来是非常类似的。策略模式和状态模式最大的区别就是策略模式只是的条件选择只执行一次,而状态模式是随着实例参数(对象实例的状态)的改变不停地更改执行模式。换句话说,策略模式只是在对象初始化的时候更改执行模式,而状态模式是根据对象实例的周期时间而动态地改变对象实例的执行模式。
注: 设计模式—状态
Design Pattern—State
状态设计模式允许一个对象实例因为一个内部的状态改变而改变其执行模式。 因此,对象实例自身可以有效地改变其类定义。
油漆工设计模式(见第十二章) 在概念上正好和策略模式是相反的。借用GoF的一个推论,策略模式改变的是一个对象实例的核心的复杂操作,而油漆工设计模式改变的是一个对象实例的皮肤。
最后一个相关的设计模式是访问者设计模式。在策略模式里面,你创建一个实际的选择的策略的对象实例然后把它绑定到一个实例参数中;在访问者模式里面,策略使用参数的方式进行传递的。你可以想象下访问者设计模式,它的设计思路和策略模式正好相反。