七. PHP模式设计----执行及描述任务
1. 解析器模式
//解析器内容类
//用于存放表达式的运算结果,并且能根据传入的表达式返回当初记录的结果
class InterpreterContext{
private $expressionstore=array();
//将对象放进array里面,索引号是对象ID号
function replace(Expression $exp,$value){
$this->expressionstore[$exp->getKey()]=$value;
}
function lookup(Expression $exp){
return $this->expressionstore[$exp->getKey()];
}
}
abstract class Expression{
private static $keyCount=0;
private $key;
abstract function interpret(InterpreterContext $context);
//每一个对象都有一个唯一的key
function getKey(){
if(!isset($this->key)){
$this->key=++self::$keyCount;
}
return $this->key;
}
}
//字符串
class LiteralExpression extends Expression{
private $value;
function __construct($value){
$this->value=$value;
}
function interpret(InterpreterContext $context){
$context->replace($this, $this->value);
}
}
//变量
class VariableExpression extends Expression{
private $value;
private $name;
function __construct($name,$value=null){
$this->name=$name;
$this->value=$value;
}
function setValue($value){
$this->value=$value;
}
function getKey(){
return $this->name;
}
function interpret(InterpreterContext $context){
if(!is_null($this->value)){
$context->replace($this, $this->value);
//设置为空,可以避免在没有修改value的情况下再次调用该方法的时候,
//也第二次调用了InterpreterContext::replece
$this->value=null;
}
}
}
//操作符表达式 抽象基类
abstract class OperatorExpression extends Expression{
//左右操作数
protected $l_op;
protected $r_op;
function __construct(Expression $l_op,Expression $r_op){
$this->l_op=$l_op;
$this->r_op=$r_op;
}
function interpret(InterpreterContext $context){
$this->l_op->interpret($context);
$this->r_op->interpret($context);
$result_l=$context->lookup($this->l_op);
$result_r=$context->lookup($this->r_op);
$this->doInterpret($context, $result_l, $result_r);
}
protected abstract function doInterpret(InterpreterContext $context,$result_l,$result_r);
}
//相等表达式
class EqualsExpression extends OperatorExpression{
//计算后将结果保存进this里...
protected function doInterpret(InterpreterContext $context,$result_l,$result_r){
$context->replace($this, $result_l==$result_r);
}
}
//布尔或表达式
class BooleanOrExpression extends OperatorExpression{
protected function doInterpret(InterpreterContext $context, $result_l, $result_r){
$context->replace($this, $result_l || $result_r);
}
}
//布尔与表达式
class BooleanAndExpression extends OperatorExpression{
protected function doInterpret(InterpreterContext $context, $result_l, $result_r){
$context->replace($this, $result_l && $result_r);
}
}
/*
$context=new InterpreterContext();
$literal=new LiteralExpression("Bob");
$literal->interpret($context);
print $context->lookup($literal);//Bob
$context=new InterpreterContext();
$myvar=new VariableExpression("V","one");
$myvar->interpret($context);
print $context->lookup($myvar);//one
$myothervar=new VariableExpression("V");
$myvar->interpret($context);
print $context->lookup($myvar);//one
*/
//利用上面的代码来检测迷你型语言
//$input equals "4" or $input equals "four"
$context=new InterpreterContext();
//一个没有赋值的变量
$input=new VariableExpression('input');
//定义一个复杂的布尔型变量,初始化的参数被保存在l_op,r_op里面
//注意,此时他们还没有进行相关运算,必须等到调用布尔型变量的interpret()
//之后,布尔型变量里面的参数才会各自调用自身的interpret,形成一个调用栈...
$statement=new BooleanOrExpression(
new EqualsExpression($input, new LiteralExpression('four')),
new EqualsExpression($input, new LiteralExpression('4')));
$statement->interpret($context);
print $context->lookup($statement);
foreach (array('four','4','52') as $val){
$input->setValue($val);
print "$val:\n";
$statement->interpret($context);
if($context->lookup($statement)){
print "top marks\n";
}else{
print "dunce hat on\n\n";
}
}
/*four:
top marks
4:
top marks
52:
dunce hat on*/
2.策略模式
将类中许多不同操作而且是来自同一个接口的算法,独立起来封装在一个新类中,主类在由分类组合或者聚合而成
//Question类将由Marker聚合而成
abstract class Question{
protected $prompt;
protected $marker;
function __construct($prompt,Marker $marker){
$this->marker=$marker;
$this->prompt=$prompt;
}
//利用委托实现
function mark($response){
return $this->marker->mark($response);
}
}
class TextQuestion extends Question{
//处理文本问题特有的操作
}
class AVQuestion extends Question{
//处理语音问题特有操作
}
//策略对象
//将算法部分独立出来封装在Marker类中....
abstract class Marker{
protected $test;
function __construct($test){
$this->test=$test;
}
abstract function mark($response);
}
//MarkLogic语言
class MarkLogicMarker extends Marker{
protected $engine;
function __construct($test){
parent::__construct($test);
}
function mark($response){
//模拟返回true
return true;
}
}
//直接匹配
class MatchMarker extends Marker{
function mark($response){
return ($this->test==$response);
}
}
//正则表达式
class RegexpMarker extends Marker{
function mark($response){
return (preg_match($this->test, $response));
}
}
//客户端示例代码
$markers=array(new RegexpMarker("/f.ve/"),
new MatchMarker("fivev"),
new MarkLogicMarker('$input equals "five"'));
foreach($markers as $marker){
print get_class($marker)."\n";
//新建一个问题,将marker实例传进去
$question=new TextQuestion("How many beans make five", $marker);
foreach (array("five","four") as $response){
print "\t response:$response: ";
if($question->mark($response)){
print "well done\n";
}else{
print "never mind\n";
}
}
}
/*RegexpMarker
response:five: well done
response:four: never mind
MatchMarker
response:five: never mind
response:four: never mind
MarkLogicMarker
response:five: well done
response:four: well done
*/
3 观察者模式
观察者模式的和兴是把客户元素(观察者)从一个中心类中分离开来.当主体中有事件发生时,观察者必须被通知到!同时观察者和主体类不是通过硬编码实现,而是通过接口组合聚合实现
*问题
// Login类(主体类)
class Login {
const LOGIN_USER_UNKNOWN = 1;
const LOGIN_WRONG_PASS = 2;
const LOGIN_ACCESS = 3;
private $status = array ();
// 登录操
function handleLogin($user, $pass, $ip) {
$ret=false;
switch (rand ( 1, 3 )) {
case 1 :
$this->setStatus ( self::LOGIN_ACCESS, $user, $ip );
$ret=ture;
break;
case 2 :
$this->setStatus ( self::LOGIN_WRONG_PASS, $user, $ip );
$ret=false;
break;
case 3 :
$this->setStatus ( self::LOGIN_USER_UNKNOWN, $user, $ip );
$ret=false;
break;
default:
$ret=false;
}
//如果需要记录IP时
Logger::logIP($user, $ip, $this->getStatus());
//如果需要把登录失败的人的IP发送到管理员邮箱时...
if(!$ret){
Notifier::mailWarning($user, $ip, $this->getStatus());
}
//还需要其他功能时,比如特殊IP需要设置cookie等...
//需要在这里面一直添加...
return $ret;
}
function setStatus($status, $user, $ip) {
$this->status = array ($status,$user,$ip);
}
function getStatus(){
return $this->status;
}
}
class Logger{
static function logIP($user,$ip,$status){}
}
class Notifier{
static function mailWarning($user,$ip,$status){}
}
*利用观察者模式,使代码改动少些....
// 主体类的抽象接口
interface Observable{
//附加
function attach(Observer $observer);
//删除
function detach(Observer $observer);
//通知
function notify();
}
// Login类(主体类)
class Login implements Observable{
const LOGIN_USER_UNKNOWN = 1;
const LOGIN_WRONG_PASS = 2;
const LOGIN_ACCESS = 3;
private $observers;
private $status = array ();
// 登录操作
function handleLogin($user, $pass, $ip) {
$ret=false;
switch (rand ( 1, 3 )) {
case 1 :
$this->setStatus ( self::LOGIN_ACCESS, $user, $ip );
$ret=ture;
break;
case 2 :
$this->setStatus ( self::LOGIN_WRONG_PASS, $user, $ip );
$ret=false;
break;
case 3 :
$this->setStatus ( self::LOGIN_USER_UNKNOWN, $user, $ip );
$ret=false;
break;
default:
$ret=false;
}
//使用观察者模式之后,如果需要增加新功能,只需要在本类中添加观察者实例即可...
$this->notify();
return $ret;
}
function setStatus($status, $user, $ip) {
$this->status = array ($status,$user,$ip);
}
function getStatus(){
return $this->status;
}
function attach(Observer $observer){
$this->observers[]=$observer;
}
function detach(Observer $observer){
$newObserver=array();
foreach ($this->observers as $obs){
if($obs!==$observer){
$newObserver[]=$obs;
}
}
$this->observers=$newObserver;
}
//通知所有观察者
function notify(){
foreach ($this->observers as $bos){
$bos->update($this);
}
}
}
//观察者接口
interface Observer{
function update(Observable $observable);
}
class SecurityMonitor implements Observer{
function update(Observable $observable){
//getStatus()不是Observable接口规定的方法
$status=$observable->getStatus();
//对status的判断也涉及到耦合问题
if($status[0]==Login::LOGIN_WRONG_PASS){
//发送邮件给管理员
print __CLASS__.":\t sending mail to sysadmin\n";
}
}
}
$login=new Login();
$login->attach(new SecurityMonitor());
$login->handleLogin("coco", "123456", "127.0.0.1");//有可能输出发送消息到管理员
*改进后的观察者模式
//可观察元素:主体类的基接口
interface Observable{
//附加
function attach(Observer $observer);
//删除
function detach(Observer $observer);
//通知
function notify();
}
// Login类(主体类)
class Login implements Observable{
const LOGIN_USER_UNKNOWN = 1;
const LOGIN_WRONG_PASS = 2;
const LOGIN_ACCESS = 3;
private $observers;
private $status = array ();
// 登录操作
function handleLogin($user, $pass, $ip) {
$ret=false;
switch (rand ( 1, 3 )) {
case 1 :
$this->setStatus ( self::LOGIN_ACCESS, $user, $ip );
$ret=ture;
break;
case 2 :
$this->setStatus ( self::LOGIN_WRONG_PASS, $user, $ip );
$ret=false;
break;
case 3 :
$this->setStatus ( self::LOGIN_USER_UNKNOWN, $user, $ip );
$ret=false;
break;
default:
$ret=false;
}
//使用观察者模式之后,如果需要增加新功能,只需要在本类中添加观察者实例即可...
$this->notify();
return $ret;
}
function setStatus($status, $user, $ip) {
$this->status = array ($status,$user,$ip);
}
function getStatus(){
return $this->status;
}
function attach(Observer $observer){
$this->observers[]=$observer;
}
function detach(Observer $observer){
$newObserver=array();
foreach ($this->observers as $obs){
if($obs!==$observer){
$newObserver[]=$obs;
}
}
$this->observers=$newObserver;
}
//通知所有观察者
function notify(){
foreach ($this->observers as $bos){
$bos->update($this);
}
}
}
//观察者接口
interface Observer{
function update(Observable $observable);
}
//Login:观察者超类(用于解决直接在Observer中调用具体的Observable子类造成的风险)
abstract class LoginObserver implements Observer{
private $login;
function __construct(Login $login){
$this->login=$login;
$this->login->attach($this);
}
//该方法在Observable的子类某些方法被调用时触发
function update(Observable $observable){
//触发该方法时,传入的参数必须是Login才有效...
if($this->login===$observable){
$this->doUpdate($observable);
}
}
abstract function doUpdate(Login $login);
}
//改进后的观察者能保证调用的Observable实例方法一定存在
class SecurityMonitor extends LoginObserver{
function doUpdate(Login $login){
$status=$login->getStatus();
//对status的判断也涉及到耦合问题
if($status[0]==Login::LOGIN_WRONG_PASS){
//发送邮件给管理员
print __CLASS__.":\t sending mail to sysadmin\n";
}
}
}
//日常记录
class GeneralLogger extends LoginObserver{
function doUpdate(Login $login){
$status=$login->getStatus();
//添加记录到log中
//...
print __CLASS__."\t add login data to log\n";
}
}
//合作伙伴工具类
class PartnershipTool extends LoginObserver{
function doUpdate(Login $login){
$status=$login->getStatus();
//检查IP,如果匹配,则设置cookie...
//...
print __CLASS__."\t set cookie if IP matches a list\n";
}
}
//客户端代码有一点点改变...
$login=new Login();
new SecurityMonitor($login);
new GeneralLogger($login);
new PartnershipTool($login);
$login->handleLogin("coco", "123456", "127.0.0.1");
/*
* 有可能输出
*SecurityMonitor: sending mail to sysadmin
*GeneralLogger add login data to log
*PartnershipTool set cookie if IP matches a list
**/
4. 访问者模式
当使用对象集合时,我们可能需要对结构上每一个单独的组件应用各种操作.这样的操作可以内建于组件本身,毕竟组件内部调用其他组件是最方便的.
但是这种方法也存在问题,因为我们并不知道所有可能需要的执行的操作.如果每增加一个操作,就在类中增加一个对于新操作的支持,类就会变得越来越臃肿.访问者模式可以解决这个问题.
//本例是基于"文明"游戏的代码建立而成...
//*战斗单元类
abstract class Unit{
//深度
protected $depth=0;
//攻击强度
abstract function bombardStrength();
function getComposite(){
return null;
}
//为访问者模式打造的方法
function accept(ArmyVisitor $visitor){
//构建一个根据自己规则定义的方法名,然后交给visitor自身调用
$method="visit".get_class($this);
$visitor->$method($this);
}
//用于计算Unit在对象树中的深度
protected function setDepth($depth){
$this->depth==$depth;
}
function getDepth(){
return $this->depth;
}
}
//复合抽象类
abstract class CompositeUnit extends Unit{
protected $units=array();
//为访问者模式打造的方法
function accept(ArmyVisitor $visitor){
//构建一个根据自己规则定义的方法名,然后交给visitor自身调用
//先调用父类accept(),再遍历调用子元素accept()
parent::accept($visitor);
foreach ($this->units as $thisunit){
$thisunit->accept($visitor);
}
}
//可以不用写bombardStrength()
function getComposite(){
return $this;
}
//添加单元
//同时标记节点在对象树中的深度
function addUnit(Unit $unit){
if(!empty($unit)){
if(in_array($unit, $this->units,true)){
return;
}
//可以用下面代码替换in_array()函数
// foreach ($this->units as $thisunit){
// if($thisunit===$unit){
// return;
// }
// }
//计算好深度先...
$unit->setDepth($this->depth+1);
array_push($this->units, $unit);
}
}
function removeUnit(Unit $unit){
$this->units=array_udiff($this->units, array($unit), function ($a,$b){return ($a===$b?0:1);});
}
}
//射手
class Archer extends Unit{
function bombardStrength(){
return 4;
}
}
//激光塔
class LaserCannonUnit extends Unit{
function bombardStrength(){
return 44;
}
}
//军队:由战斗单元组成
class Army extends CompositeUnit{
//计算总强度
function bombardStrength(){
$ret=0;
foreach ($this->units as $unit){
$ret+=$unit->bombardStrength();
}
return $ret;
}
//移动能力,防御能力...省略
}
//运兵船:一个类似军队的单元,它具备10个战斗单元,有攻击力
class TroopCarrier extends CompositeUnit{
//具备和军队差不多的方法和属性
function bombardStrength(){
//Do something...
}
}
//军队访问者基类
abstract class ArmyVisitor{
//相关方法待会写...
//Army可能有多少种Unit,这里就有多少个visit方法...
//方法命名规则为visit+类名
//默认的visit
abstract function visit(Unit $unit);
function visitArcher(Archer $node){
$this->visit($node);
}
function visitLaserCannonUnit(LaserCannonUnit $node){
$this->visit($node);
}
function visitArmy(Army $node){
$this->visit($node);
}
function visitTroopCarrier(TroopCarrier $node){
$this->visit($node);
}
}
//具体的Army访问者类,用于转存文本
class TextDumpArmyVisitor extends ArmyVisitor{
private $text="";
function visit(Unit $node){
$ret="";
$pad=4*$node->getDepth();
$ret.=sprintf("%{$pad}s","");
$ret.=get_class($node).":";
$ret.="bombard: ".$node->bombardStrength()."\n";
$this->text.=$ret;
}
function getText(){
return $this->text;
}
}
//客户端实例代码
$army=new Army();
$army->addUnit(new Archer());
$army->addUnit(new LaserCannonUnit());
$textdump=new TextDumpArmyVisitor();
$army->accept($textdump);
print $textdump->getText();
/*
* Army:bombard: 48
* Archer:bombard: 4
* LaserCannonUnit:bombard: 44
*/
5. 命令模式
//命令模式
//命令模式最初来源于图形化用户界面设计,但现在广泛应用于企业应用设计,特别促进了控制器(请求和分法处理)
//和领域模型(应用逻辑)的分离.说得更简单一点,命令模式有助于系统更好地进行组织,并易于扩展
//Command可以设计成接口,因为它很简单...
//Commands/Command.php
abstract class Command{
abstract function execute(CommandContext $context);
}
require_once("Command.php");
class Registry{
//一个空类...
static function getAccessManager(){
return new AccessManager();
}
}
class AccessManager{
function login(){
return new stdClass();
}
function getError(){}
}
class LoginCommand extends Command{
function execute(CommandContext $context){
$manager=Registry::getAccessManager();
$user=$context->get('username');
$user=$context->get('pass');
//虚构出来的空类空方法
$user_obj=$manager->login();
if(is_null($user_obj)){
$context->setError($manager->getError());
return false;
}
$context->addParam("user", $user);
return true;
}
}
//CommandContext类用来做任务扩增用,在这儿主要功能是传递数据给Command类
class CommandContext{
private $params=array();
private $error="";
function __construct(){
$this->params=$_REQUEST;
}
function addParam($key,$val){
$this->params[$key]=$val;
}
function get($key){
return $this->params[$key];
}
function setError($error){
$this->error=$error;
}
function getError(){
return $this->error;
}
}
//客户端代码(用于创建命令)已经调用者代码
class CommandNotFoundException extends Exception{
}
//创建命令的工厂
class CommandFactory{
private static $dir="Commands";
//根据参数action,以及类文件存放目录$dir动态创建相应的$action+Command类
static function getCommand($action='default'){
//匹配是否出现非法字符(非字母数字下划线)
if(preg_match('/\W/', $action)){
throw new Exception("Illegal characters in action");
}
$class=ucfirst(strtolower($action)."Command");
$file=self::$dir.DIRECTORY_SEPARATOR."{$class}.php";
if(!file_exists($file)){
throw new CommandNotFoundException("File could not be find !");
}
require_once("$file");
if(!class_exists($class)){
throw new CommandNotFoundException("Class could not be find !");
}
return new $class();
}
}
//调用者,里面包含一个CommandContext对象实例,用于存放web请求的数据
class Controller{
private $context;
function __construct(){
$this->context=new CommandContext();
}
function getContext(){
return $this->context;
}
function process(){
$cmd=CommandFactory::getCommand($this->getContext()->get('action'));
if(!$cmd->execute($this->context)){
//处理失败
print "Faile in process!";
}else{
//处理成功,可以显示相应的视图层
print "Success in process!";
}
}
}
$controller=new Controller();
$context=$controller->getContext();
$context->addParam('action', 'Login');
$context->addParam('user', 'cocos');
$context->addParam('pass', 'tiddles');
//controller执行process方法,需要不理解command的意义.
$controller->process();//Success in process