七. 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