七. PHP模式設計----運行及描寫敘述任務
阿新 • • 發佈:2017-07-30
pda data 實例方法 warning 特殊 發送消息 dmi 才會 roc
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)); } } //client演示樣例代碼 $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"; } } //client代碼有一點點改變... $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; } } //client實例代碼 $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能夠設計成接口,由於它非常easy... //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; } } //client代碼(用於創建命令)已經調用者代碼 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
七. PHP模式設計----運行及描寫敘述任務