設計我的php框架須知,phpms雛形

phpms框架源碼 github.com/wuxiumu/msphp

1、PHP經常使用的四種數據結構

簡介:spl是php的一個標準庫。html

官方文檔:php.net/manual/zh/b…mysql

<?php
 
//spl(php標準庫)數據結構
 
/**
 * 棧(先進後出)
 */
function zan(){
    $stack = new SplStack();
    $stack->push('data1');//入棧(先進後出)
    $stack->push('data2');//入棧
    $stack->push('data3');//入棧
     
    echo $stack->pop().PHP_EOL;//出棧
    echo $stack->pop().PHP_EOL;//出棧
    echo $stack->pop().PHP_EOL;//出棧
}
 
 /**
 *隊列(先進先出)
 */

function duilie(){
    $queue = new SplQueue();
    $queue->enqueue('data4');//入隊列
    $queue->enqueue('data5');//入隊列
    $queue->enqueue('data6');//入隊列
    
    echo $queue->dequeue().PHP_EOL;//出隊列
    echo $queue->dequeue().PHP_EOL;//出隊列
    echo $queue->dequeue().PHP_EOL;//出隊列
}
 
/**
 * 堆
 */
function dui(){
    $heap = new SplMinHeap();
    $heap->insert('data8');//入堆
    $heap->insert('data9');//入堆
    $heap->insert('data10');//入堆
    
    echo $heap->extract().PHP_EOL;//從堆中提取數據
    echo $heap->extract().PHP_EOL;//從堆中提取數據
    echo $heap->extract().PHP_EOL;//從堆中提取數據
}
  
/**
 * 固定數組(不論使不使用,都會分配相應的內存空間)
 */
$array = new SplFixedArray(15);
$array['0'] = 54;
$array['6'] = 69;
$array['10'] = 32;
var_dump($array);

複製代碼

2、PHP鏈式操做的實現(原理)

一、入口文件

index.phpgit

header("content-type:text/html;charset=utf-8"); 
define('PHPMSFRAME',__DIR__);                   
define('CORE',PHPMSFRAME.'/core');
define('APP',PHPMSFRAME.'/app');
define('MODULE','app');

define('DEBUG',true);
 
include "vendor/autoload.php";

if(DEBUG){
	$whoops = new \Whoops\Run;
	$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
	$whoops->register();
	ini_set('display_error', 'On');
}else{
	ini_set('display_error', 'Off');
}

include CORE.'/common/function.php';

include CORE.'/phpmsframe.php';

spl_autoload_register('\core\phpmsframe::load');

\core\phpmsframe::run();
複製代碼

二、自動加載類

core\phpmsframe.phpgithub

<?php

namespace core;

class phpmsframe 
{
	public static $classMap = array();

	public $assign; 
 
	static public function run()
	{		
		$route = new \core\lib\route();
		$ctrlClass = $route->ctrl;
		$action = $route->action;		
		$ctrlfile = APP.'/ctrl/'.$ctrlClass.'Ctrl.php';
		$ctrlClass = '\\'.MODULE.'\ctrl\\'.$ctrlClass.'Ctrl';
		if(is_file($ctrlfile)){			
			include $ctrlfile;			
			$ctrl = new $ctrlClass();
			$ctrl->$action();					
		}else{	
			$msg = "控制器 $ctrlClass 不存在\n";		 
			self::reportingDog($msg);			
		}
	}

	static public function load($class)
	{
		if(isset($classMap[$class])){
			return true;
		}else{
			$class  = str_replace('\\', '/', $class);
			$file = PHPMSFRAME.'/'.$class.'.php';
			if(is_file($file)){
				include $file;
				self::$classMap[$class] = $class; 
			}else{
				return false;
			}
		}
	}

	public function assign($name,$value){
		$this->assign[$name]=$value;
	}

	public function display($file){
		$file_path = APP.'/views/'.$file;
		if(is_file($file_path)){	
			/***********twig模板***********/
			$loader = new \Twig_Loader_Filesystem(APP.'/views');
			$twig = new \Twig_Environment($loader, array(
			    'cache' => PHPMSFRAME.'/cache',
			    'debug'=>DEBUG,
			));						
			$template = $twig->load($file);			
			$template->display($this->assign?$this->assign:'');
			/***********twig模板end***********/

			/***********原生模板***********/
			//extract($this->assign);
			//include $file_path;
			/***********原生模板end***********/
		}
	}

	static private function reportingDog($msg){
		echo $msg."\n";		
		include 'smile/havefun.php';		
		$num = str_pad(rand(00,32),2,"0",STR_PAD_LEFT);
		$num = "str_".$num;		
		$Parsedown = new \Parsedown();
		echo $Parsedown->text($$num);
		$num = "str_".rand(50,84);
		echo $Parsedown->text($$num); 
		exit;
	}
}

複製代碼

三、數據庫類

注:只是原理,並無對方法進行具體的封裝,具體的封裝仍是看我的喜愛去定鏈式查詢的風格。redis

core\lib\model.php算法

<?php

namespace core\lib;

use  core\lib\drive\database\medooModel;

class model extends medooModel
{	
	
}
複製代碼

core\lib\drive\database\medooModel.phpsql

<?php
/**
 * 繼承medoo第3方類庫,模型基類
 */
namespace core\lib\drive\database;

use core\lib\conf;

use Medoo\Medoo;

class medooModel extends  Medoo{
    //初始化,繼承pdo應該是就能夠直接用手冊中的pdo中的方法了
    public function __construct()
    {
		$option = conf::all('database');		
		parent::__construct($option['mysql_medoo_conf']);
    }
}
複製代碼

3、PHP魔術方法的使用

在php設計模式中,會涉及到不少魔術方法的使用,這裏也對常常會用到的魔術方法進行簡單總結。數據庫

一、框架入口文件

core\phpmsframe.php編程

/**
    * 魔術方法的使用
    */
 
   /* 這是一個魔術方法,當一個對象或者類獲取其不存在的屬性的值時,
    * 如:$obj = new BaseController ;
    * $a = $obj -> a ;
    * 該方法會被自動調用,這樣作很友好,能夠避免系統報錯
    */
    public function __get($property_name){
        $msg = "屬性 $property_name 不存在\n";
        self::reportingDog($msg);	
    }

   /* 這是一個魔術方法,當一個對象或者類給其不存在的屬性賦值時,
    * 如:$obj = new BaseController ;
    * $obj -> a = 12 ;
    * 該方法(__set(屬性名,屬性值))會被自動調用,這樣作很友好,能夠避免系統報錯
    */
    public function __set($property_name,$value){
        $msg = "屬性 $property_name 不存在\n";
		self::reportingDog($msg);	
    }

   /* 這是一個魔術方法,當一個對象或者類的不存在屬性進行isset()時,
    * 注意:isset 用於檢查一個量是否被賦值 若是爲NULL會返回false
    * 如:$obj = new BaseController ;
    * isset($obj -> a) ;
    * 該方法會被自動調用,這樣作很友好,能夠避免系統報錯
    */
    public function __isset($property_name){
        $msg = "屬性 $property_name 不存在\n";
        self::reportingDog($msg);	
    }

   /* 這是一個魔術方法,當一個對象或者類的不存在屬性進行unset()時,
    * 注意:unset 用於釋放一個變量所分配的內存空間
    * 如:$obj = new BaseController ;
    * unset($obj -> a) ;
    * 該方法會被自動調用,這樣作很友好,能夠避免系統報錯
    */
    public function __unset($property_name){
        $msg = "屬性 $property_name 不存在\n";
        self::reportingDog($msg);	
    }

    /* 當對這個類的對象的不存在的實例方法進行「調用」時,會自動調用該方法,
     * 這個方法有2個參數(必須帶有的):
     * $methodName 表示要調用的不存在的方法名;
     * $argument 是一個數組,表示要調用該不存在的方法時,所使用的實參數據,
     */
    public function __call($methodName,$argument){
		$msg = "實例方法 $methodName 不存在\n";
        self::reportingDog($msg);	
    }
複製代碼

4、三種基礎設計模式

一、工廠模式

經過傳入參數的不一樣,來實例化不一樣的類。

$cache =\Core\extend\CacheFactory::getCacheObj('redis',array(
			'host' => '127.0.0.1',
			'pass' => 'myRedis&&&'
		));
var_dump($cache);
複製代碼

core\extend\CacheFactory.php

<?php

namespace core\extend;

class CacheFactory
{
    const FILE = 1;
    const MEMCACHE = 2;
    const REDIS = 3;
 
    static $instance;//定義靜態屬性,用於存儲對象
 
    /**
     * 工廠類建立緩存對象
     * @param $type 指定緩存類型
     * @param array $options 傳入緩存參數
     * @return FileCache|Memcache|RedisCache
     */
    static function getCacheObj($type, array $options)
    {
        switch ($type) {
            case 'file':
            case self::FILE:
                self::$instance = new FileCache($options);
                break;
 
            case 'memcache':
            case self::MEMCACHE:
                self::$instance = new Memcache($options);
                break;
 
            case 'redis':
            case self::REDIS:
                self::$instance = new RedisCache($options);
                break;
 
            default:
                self::$instance = new FileCache($options);
                break;
 
        }
        return self::$instance;
    }
}
複製代碼

二、單例模式

保證一個類只實例化一個類對象,進而減小系統開銷和資源的浪費

//單例模式建立對象
$obj = Extend\SingleObject::getInstance();
$obj2 = Extend\SingleObject::getInstance();
var_dump($obj,$obj2);//從結果能夠看出,兩個實例化的對象實際上是一個對象
複製代碼

core\extend\SingleObject.php

<?php

namespace core\extend;

class SingleObject
{
    //私有的靜態屬性,用於存儲類對象
    private static $instance = null;
    
    //私有的構造方法,保證不容許在類外 new
    private function __construct(){

    }

    //私有的克隆方法, 確保不容許經過在類外 clone 來建立新對象
    private function __clone(){
        
    }

    //公有的靜態方法,用來實例化惟一當前類對象
    public static function getInstance()
    {
        if(is_null(self::$instance)){
            self::$instance = new self;
        }
        return self::$instance;
    }
}
複製代碼

三、註冊樹模式

將咱們用到的對象註冊到註冊樹上,而後在以後要用到這個對象的時候,直接從註冊樹上取下來就好。(就和咱們用全局變量同樣方便)

core\extend\RegisterTree,php

<?php

namespace core\extend;

class RegisterTree
{
    static protected $objects;//靜態類屬性,用於儲存註冊到註冊樹上的對象
 
    /**
     * 將對象註冊到註冊樹上
     * @param $alias 對象的別名
     * @param $object 對象
     */
    static function setObject($alias,$object)
    {
        self::$objects[$alias] = $object;
    }
 
    /**
     * 從註冊樹上取出給定別名相應的對象
     * @param $alias 將對象插入到註冊樹上時寫的別名
     * @return mixed 對象
     */
    static protected function getObject($alias)
    {
        return self::$objects[$alias];
    }
 
    /**
     * 將對象從註冊樹上刪除
     * @param $alias 將對象插入到註冊樹上時寫的別名
     */
    public function unsetObject($alias)
    {
        unset(self::$objects[$alias]);
    }
}
複製代碼

關於註冊樹模式,這裏推薦一篇文章 ,能夠方便理解。 segmentfault.com/a/119000000…

5、其餘常見的8種PHP設計模式

一、適配器模式

將一個類的接口轉換成客戶但願的另外一個接口,適配器模式使得本來的因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。 應用場景:老代碼接口不適應新的接口需求,或者代碼不少很亂不便於繼續修改,或者使用第三方類庫。

常見的有兩種適配器,分別是類適配器和對象適配器,這裏拿更看好的對象適配器舉例:

<?php
namespace Extend;
 
/**
 * 對象適配器模式具體流程
 * 一、根據需求定義接口,進而知足新需求功能
 * 二、定義新類,繼承並實現定義的接口
 * 三、在實現接口時,原有的功能,只經過原有類對象調用原有類功能(委託)
 * 四、再根據需求,在新類中實現新需求功能
 * 【適用性】
 * (1)你想使用一個已經存在的類,而它的接口不符合你的需求
 * (2)你想建立一個能夠複用的類,該類能夠與其餘不相關的類或不可預見的類協同工做
 * (3)你想使用一個已經存在的子類,可是不可能對每個都進行子類化以匹配它們的接口。對象適配器能夠適配它的父類接口(僅限於對
 */
 
 
/**
 * 目標角色(根據需求定義含有舊功能加上新功能的接口)
 * Interface Target 咱們指望獲得的功能類
 * @package Extend
 */
interface Target
{
    public function simpleMethod1();
    public function simpleMethod2();
}
 
/**
 * 源角色(在新功能提出以前的舊功能類和方法)
 * Class Adaptee
 * @package Extend
 */
class Adaptee
{
 
    public function simpleMethod1()
    {
        echo 'Adapter simpleMethod1'."<br>";
    }
 
}
 
/**
 * 類適配器角色(新定義接口的具體實現)
 * Class Adapter
 * @package Extend
 */
class Adapter implements Target
{
 
    private $adaptee;
 
    function __construct()
    {
        //適配器初始化直接new 原功能類,以方便以後委派
        $adaptee = new Adaptee();
        $this->adaptee = $adaptee;
    }
 
    //委派調用Adaptee的sampleMethod1方法
    public function simpleMethod1()
    {
        echo $this->adaptee->simpleMethod1();
    }
 
    public function simpleMethod2()
    {
        echo 'Adapter simpleMethod2'."<br>";
    }
 
}
 
/**
 * 客戶端調用
 */
$adapter = new Adapter();
$adapter->simpleMethod1();
$adapter->simpleMethod2();

複製代碼

這篇文章介紹了類適配器的使用,感興趣的能夠了解一下 segmentfault.com/a/119000000…

二、策略模式

將一組特定的行爲和算法封裝成類,以適應某些特定的上下文環境,這種模式就是策略模式,策略模式能夠實現依賴倒置以及控制反轉。

實例舉例:假如一個電商網站系統,針對男性女性用戶要各自跳轉到不一樣的商品類目,而且全部的廣告位展現展現不一樣的廣告。

<?php
 
/**
 * 首頁數據控制器
 * Class Index
 */
class Home
{
    /**
     * 最好寫上這個註釋,告訴phpstorm是對應的哪一個接口類,不然雖然程序執行正確,但phpstorm識別不了
     * @var \Extend\UserType
     */
    protected $userType;
 
    /**
     * 首頁展現數據
     * 使用策略模式
     * Index constructor.
     */
    function index()
    {
        echo "AD:";
        $this->userType->showAd();
        echo "Category:";
        $this->userType->showCategory();
    }
 
    /**
     * 策略模式
     * 根據傳遞的用戶性別展現不一樣類別數據
     * @param \Extend\UserType $userType
     */
    function setUserType(\Extend\UserType $userType)
    {
        $this->userType = $userType;
    }
 
}
 
$obj = new Home();
if ($_GET['userType'] == 'female'){
    $userType = new \Extend\FemaleUserType();
} else {
    $userType = new \Extend\MaleUserType();
}
$obj->setUserType($userType);
$obj->index();

複製代碼

Extend/userType.php(定義的接口)

<?php
 
namespace Extend;
 
/**
 * 策略模式
 * 定義根據性別不一樣展現不一樣商品類目和廣告接口
 * Interface UserType
 * @package Extend
 */
interface UserType
{
    //顯示廣告
    function showAd();
    //展現類目
    function showCategory();
 
}

複製代碼

MaleUserType.php、FemaleUserType.php(具體實現的類 )

<?php
 
namespace Extend;
 
/**
 * 定義男性商品類目和廣告位數據接口
 * Class MaleUserType
 * @package Extend
 */
class MaleUserType implements UserType
{
    /**
     * 廣告欄數據展現
     */
    function showAd()
    {
        echo "this is 男性’s 廣告條目數據";
    }
 
    /**
     * 商品類目數據展現
     */
    function showCategory()
    {
        echo "this is 男性’s 商品類目數據";
    }
 
}

複製代碼
<?php
 
namespace Extend;
 
/**
 * 定義女性商品類目和廣告位數據接口
 * Class FemaleUserType
 * @package Extend
 */
class FemaleUserType implements UserType
{
 
    /**
     * 廣告欄數據展現
     */
    function showAd()
    {
        echo "this is 女性’s 廣告條目數據";
    }
 
    /**
     * 商品類目數據展現
     */
    function showCategory()
    {
        echo "this is 女性’s 商品類目數據";
    }
 
 
}

複製代碼

三、數據對象映射模式

將對象和數據存儲映射起來,對一個對象的操做會映射爲對數據存儲的操做。

下面在代碼中實現數據對象映射模式,咱們將實現一個ORM類,將複雜的sql語句映射成對象屬性的操做。並結合使用數據對象映射模式、工廠模式、註冊模式。

(1)數據庫映射模式簡單實例實現

<?php
 
//使用數據對象映射模式代替寫sql
$user = new Extend\User(25);
$user->name = '小卜丟飯糰子';
$user->salary = '20000';
$user->city = '浙江省';
 
複製代碼

Extend/User.php

<?php
 
namespace Extend;
 
class User
{
    //對應數據庫中的4個字段
    public $id;
    public $name;
    public $salary;
    public $city;
    //存儲數據庫鏈接對象屬性
    protected $pdo;
 
    public $data;
 
    function __construct($id)
    {
        $this->id = $id;
        $this->pdo = new \PDO('mysql:host=127.0.0.1;dbname=test','root','123456');
    }
 
    function __destruct()
    {
        $this->pdo->query("update user set name = '{$this->name}',salary = '{$this->salary}',city = '{$this->city}' where id='{$this->id}'");
    }
}

複製代碼

這樣,執行index.php文件,數據庫就會發生相應的操做,也就實現了基本的數據對象映射。

(2)數據庫映射模式複雜案例實現

<?php
 
class EX
{
    function index()
    {
        //使用數據對象映射模式代替寫sql
        $user = Extend\Factory::getUserObj(25);
        $user->name = '小卜丟飯糰子';
        $user->salary = '20000';
        $user->city = '浙江省';
    }
 
    function test()
    {
        $user = Extend\Factory::getUserObj(25);
        $user->city = '廣東省';
    }
 
}
 
$ex = new EX();
$ex->index();

複製代碼

Extend/Factory.php

<?php
 
namespace Extend;
 
class Factory
{
    /**
     * 工廠模式建立數據庫對象,單例模式保證建立惟一db對象
     * @return mixed
     */
    static function CreateDatabaseObj()
    {
        $db = Database::getInstance();
        return $db;
    }
 
    /**
     * 工廠模式建立user對象,註冊樹模式保證建立惟一對象,避免資源浪費
     * @param $id
     * @return User|mixed
     */
    static function getUserObj($id)
    {
        $key = 'user'.$id;
        $user = RegisterTree::getObject($key);
        if (!$user) {
            $user = new User($id);
            RegisterTree::setObject($key,$user);
        }
        return $user;
    }
}

複製代碼

Extend/Register.php

<?php
 
namespace Extend;
 
/**
 * 註冊樹模式
 * Class RegisterTree
 * @package Extend
 */
class RegisterTree
{
    static protected $objects;//靜態類屬性,用於儲存註冊到註冊樹上的對象
 
    /**
     * 將對象註冊到註冊樹上
     * @param $alias 對象的別名
     * @param $object 對象
     */
    static function setObject($alias,$object)
    {
        self::$objects[$alias] = $object;
    }
 
 
    /**
     * 從註冊樹上取出給定別名相應的對象
     * @param $alias 將對象插入到註冊樹上時寫的別名
     * @return mixed 對象
     */
    static function getObject($alias)
    {
        return self::$objects[$alias];
    }
 
    /**
     * 將對象從註冊樹上刪除
     * @param $alias 將對象插入到註冊樹上時寫的別名
     */
    public function unsetObject($alias)
    {
        unset(self::$objects[$alias]);
    }
 
}

複製代碼

Extend/User.php

<?php
 
namespace Extend;
 
class User
{
    //對應數據庫中的4個字段
    public $id;
    public $name;
    public $salary;
    public $city;
    //存儲數據庫鏈接對象屬性
    protected $pdo;
 
    public $data;
 
    function __construct($id)
    {
        $this->id = $id;
        $this->pdo = new \PDO('mysql:host=127.0.0.1;dbname=test','root','123456');
    }
 
    function __destruct()
    {
        $this->pdo->query("update user set name = '{$this->name}',salary = '{$this->salary}',city = '{$this->city}' where id='{$this->id}'");
    }
}

複製代碼

這樣,就實現了稍複雜的數據對象映射模式和工廠模式、註冊樹模式相結合的案例。

四、觀察者模式

當一個對象狀態發生改變時,依賴它的對象會所有收到通知,並自動更新。

場景:一個事件發生後,要執行一連串更新操做。傳統的編程方式就是在事件的代碼以後直接加入處理邏輯,當更新的邏輯增多以後,代碼會變的難以維護。這種方式是耦合的,侵入式的,增長新的邏輯須要修改事件主體的代碼。觀察者模式實現了低耦合,非侵入式的通知與更新機制。

4.一、傳統模式舉例:

<?php
/**
 * 一個事件的邏輯控制器
 * Class Event
 */
class Event
{
    /**
     * 用戶確認訂單
     */
    function firmOrder()
    {
        //這裏假設一個事件發生了,好比用戶已經完成下單
        echo "用戶已下單<br>";
        //傳統方式是在發生一個事件以後直接進行一系列的相關處理,耦合度比較高,好比寫入日誌,給用戶發郵件等等
        echo "在用戶下單以後進行的一系列操做<br>";
    }
 
}
 
$event = new Event();
$event->firmOrder();

複製代碼

4.二、觀察者模式典型實現方式:

(1)定義2個接口:觀察者(通知)接口、被觀察者(主題)接口

(2)定義2個類,觀察者類實現觀察者接口、被觀察者類實現被觀察者接口

(3)被觀察者註冊本身須要通知的觀察者

(4)被觀察者類某個業務邏輯發生時,通知觀察者對象,進而每一個觀察者執行本身的業務邏輯。

代碼示例:

<?php
/**
 * 觀察者模式場景描述:
 * 一、購票後記錄文本日誌
 * 二、購票後記錄數據庫日誌
 * 三、購票後發送短信
 * 四、購票送抵扣卷、兌換卷、積分
 * 五、其餘各種活動等
 */
 
 
/**
 * 觀察者接口
 */
interface TicketObserver
{
    function buyTicketOver($sender, $args); //獲得通知後調用的方法
}
 
/**
 * 被觀察者接口(購票主題接口)
 */
interface TicketObserved
{
    function addObserver($observer); //提供註冊觀察者方法
}
 
 
/**
 * 主體邏輯,繼承被觀察者接口
 * Class BuyTicket
 */
class BuyTicket implements TicketObserved
{
 
    /**
     * 定義觀察者數組屬性,用於儲存觀察者
     * @var array
     */
    private $observers = array();
 
 
    /**
     * 實現被觀察者接口定義的方法(添加觀察者)
     * @param $observer 實例化後的觀察者對象
     */
    public function addObserver($observer)
    {
        $this->observers[] = $observer;
    }
 
 
    /**
     * 購票主體方法
     * BuyTicket constructor.
     * @param $ticket 購票排號
     */
    public function buyTicket($ticket)
    {
        //一、根據需求寫購票邏輯
        //..............
 
        //二、購票成功以後,循環通知觀察者,並調用其buyTicketOver實現不一樣業務邏輯
        foreach ($this->observers as $observe) {
            $observe->buyTicketOver($this, $ticket); //$this 可用來獲取主題類句柄,在通知中使用
        }
 
    }
 
}
 
 
 
/**
 * 購票成功後,發送短信通知
 * Class buyTicketMSN
 */
class buyTicketMSN implements TicketObserver
{
    public function buyTicketOver($sender, $ticket)
    {
        echo (date ( 'Y-m-d H:i:s' ) . " 短信日誌記錄:購票成功:$ticket<br>");
    }
}
 
/**
 * 購票成功後,記錄日誌
 * Class buyTicketLog
 */
class buyTicketLog implements TicketObserver
{
    public function buyTicketOver($sender, $ticket) 
    {
        echo (date ( 'Y-m-d H:i:s' ) . " 文本日誌記錄:購票成功:$ticket<br>");
    }
}
 
 
/**
 * 購票成功後,贈送優惠券
 * Class buyTicketCoupon
 */
class buyTicketCoupon implements TicketObserver
{
    public function buyTicketOver($sender, $ticket) 
    {
        echo (date ( 'Y-m-d H:i:s' ) . " 贈送優惠券:購票成功:$ticket 贈送10元優惠券1張。<br>");
    }
}
 
 
//實例化購票類
$buy = new BuyTicket();
//添加多個觀察者
$buy->addObserver(new buyTicketMSN());
$buy->addObserver(new buyTicketLog());
$buy->addObserver(new buyTicketCoupon());
//開始購票
$buy->buyTicket ("7排8號");

複製代碼

五、原型模式

原型模式與工廠模式的做用相似,都是用來建立對象的。可是實現方式是不一樣的。原型模式是先建立好一個原型對象,而後經過clone原型對象來建立新的對象。這樣,就免去了類建立時重複的初始化操做。

原型模式適用於大對象的建立,建立一個大對象須要很大的開銷,若是每次new就會消耗很大,原型模式僅需內存拷貝便可。

代碼實例:

<?php
/**
 * 抽象原型角色
 */
interface Prototype
{
    public function copy();
}
 
/**
 * 具體原型角色
 */
class ConcretePrototype implements Prototype
{
 
    private $_name;
 
    public function __construct($name)
    {
        $this->_name = $name;
    }
 
    public function setName($name)
    {
        $this->_name = $name;
    }
 
    public function getName()
    {
        return $this->_name;
    }
 
    public function copy()
    {
        //深拷貝實現
         //$serialize_obj = serialize($this); // 序列化
         //$clone_obj = unserialize($serialize_obj); // 反序列化
         //return $clone_obj;
 
        // 淺拷貝實現
        return clone $this;
    }
 
}
 
/**
 * 測試深拷貝用的引用類
 */
class Demo
{
    public $array;
}
 
 
//測試
$demo = new Demo();
$demo->array = array(1, 2);
$object1 = new ConcretePrototype($demo);
$object2 = $object1->copy();
 
var_dump($object1->getName());
echo '<br />';
var_dump($object2->getName());
echo '<br />';
 
$demo->array = array(3, 4);
var_dump($object1->getName());
echo '<br />';
var_dump($object2->getName());
echo '<br />';

複製代碼

關於原型模式文章:www.imooc.com/article/169…

六、裝飾器模式

能夠動態的添加或修改類的功能

一個類實現一個功能,若是要再修改或添加額外的功能,傳統的編程模式須要寫一個子類繼承它,並從新實現類的方法。

使用裝飾器模式,僅需在運行時添加一個裝飾器對象便可實現,能夠實現最大的靈活性。

<?php
/**
 * 裝飾器流程
 * 一、聲明裝飾器接口(裝飾器接口)
 * 二、具體類繼承並實現裝飾器接口(顏色裝飾器實現,字體大小裝飾器實現)
 * 三、在被裝飾者類中定義"添加裝飾器"方法(EchoText類中的addDecorator方法)
 * 四、在被裝飾者類中定義調用裝飾器的方法(EchoText類中的beforeEcho和afterEcho方法)
 * 五、使用時,實例化被裝飾者類,並傳入裝飾器對象(好比new ColorDecorator('yellow'))
 */
 
/**
 * 裝飾器接口
 * Class Decorator
 */
interface Decorator
{
    public function beforeEcho();
    public function afterEcho();
}
 
/**
 * 顏色裝飾器實現
 * Class ColorDecorator
 */
class ColorDecorator implements Decorator
{
    protected $color;
 
    public function __construct($color)
    {
        $this->color = $color;
    }
 
    public function beforeEcho()
    {
        echo "<dis style='color: {$this->color}'>";
    }
 
    public function afterEcho()
    {
        echo "</div>";
    }
}
 
/**
 * 字體大小裝飾器實現
 * Class SizeDecorator
 */
class SizeDecorator implements Decorator
{
    protected $size;
 
    public function __construct($size)
    {
        $this->size = $size;
    }
 
    public function beforeEcho()
    {
        echo "<dis style='font-size: {$this->size}px'>";
    }
 
    public function afterEcho()
    {
        echo "</div>";
    }
}
 
/**
 * 被裝飾者
 * 輸出一個字符串
 * 裝飾器動態添加功能
 * Class EchoText
 */
class EchoText
{
    protected $decorators = array();//存放裝飾器
 
    //裝飾方法
    public function Index()
    {
        //調用裝飾器前置操做
        $this->beforeEcho();
        echo "你好,我是裝飾器。";
        //調用裝飾器後置操做
        $this->afterEcho();
    }
 
    //添加裝飾器
    public function addDecorator(Decorator $decorator)
    {
        $this->decorators[] = $decorator;
    }
 
    //執行裝飾器前置操做 先進先出原則
    protected function beforeEcho()
    {
        foreach ($this->decorators as $decorator)
            $decorator->beforeEcho();
    }
 
    //執行裝飾器後置操做 先進後出原則
    protected function afterEcho()
    {
        $tmp = array_reverse($this->decorators);
        foreach ($tmp as $decorator)
            $decorator->afterEcho();
    }
}
 
//實例化輸出類
$echo = new EchoText();
//增長裝飾器
$echo->addDecorator(new ColorDecorator('yellow'));
//增長裝飾器
$echo->addDecorator(new SizeDecorator('22'));
//輸出
$echo->Index();

複製代碼

七、迭代器模式

在不須要了解內部實現的前提下,遍歷一個聚合對象的內部元素而又不暴露該對象的內部表示,這就是PHP迭代器模式的定義。

相對於傳統編程模式,迭代器模式能夠隱藏遍歷元素的所需的操做。

<?php

$users = new Extend\AllUser();
//循環遍歷出全部用戶數據
foreach ($users as $user) {
    var_dump($user);
}

複製代碼

Extend/AllUser.php

<?php
namespace Extend;
 
/**
 * 迭代器模式,繼承php內部自帶的迭代器接口(\Iterator)
 * Class AllUser
 * @package Extend
 */
class AllUser implements \Iterator
{
    protected $index = 0;//表示索引
    protected $ids = array();//用於儲存全部user的id(實際應用中,能夠採用註冊樹模式進行存儲)
    protected $pdo;//用於存儲數據庫對象
 
    function __construct()
    {
        //獲取pdo數據庫對象
        $this->pdo = new \PDO('mysql:host=127.0.0.1;dbname=test','root','123456');
        //獲取全部用戶的id
        $this->ids = $this->pdo->query("select id from user")->fetchAll(2);
    }
 
    /**
     * 實現接口方法,重置迭代器,回到集合開頭
     */
    public function rewind()
    {
        $this->index = 0;
    }
 
    /**
     * 實現接口方法,獲取當前元素
     * @return mixed|void
     */
    public function current()
    {
        $id = $this->ids[$this->index]['id'];
        //獲取當前用戶的數據
        $user_data = $this->pdo->query("select * from user where id='{$id}'")->fetch(2);
        return $user_data;
    }
 
    /**
     * 實現接口方法,獲取當前元素鍵值
     * @return mixed|void
     */
    public function key()
    {
        return $this->index;
    }
 
    /**
     * 實現接口方法,獲取下一個元素
     */
    public function next()
    {
        $this->index++;
    }
 
    /**
     * 實現接口方法,驗證是否還有下一個元素
     * @return bool|void
     */
    public function valid()
    {
        return $this->index < count($this->ids);
    }
 
}

複製代碼

關於php迭代器文章 www.php.cn/php-weiziji…

八、代理模式

在客戶端與實體之間創建一個代理對象(proxy),客戶端對實體進行操做所有委派給代理對象,隱藏實體的具體實現細節。

典型的應用就是mysql的主從結構,讀寫分離。在mysql中,對全部讀的操做請求從庫,全部寫的操做請求主庫。

聲明一個代理類,前臺使用時只需建立一個代理類,調用對應方法便可。代碼實例:

<?php
 
// 一、傳統編程模式是手動選擇
#查詢操做使用從庫
//$db_slave = Extend\Factory::getDatabase('slave');
//$info = $db_slave->query("select * from user where id = 1 limit 1");
#增刪改操做使用主庫
//$db_master = Extend\Factory::getDatabase('master');
//$db_master->query("update user name = 'xiaobudiu' where id = 29 limit 1");
 
 
// 二、使用代理模式
$db_proxy = new Extend\Proxy();
$db_proxy->getUserName(1);
$db_proxy->setUserName(29,'xiaobudiu');

複製代碼

Extend/Proxy.php

<?php
namespace Extend;
 
class Proxy implements IUserProxy
{
    function getUserName($id)
    {
        $db = Factory::getDatabase('slave');
        $db->query("select name from user where id =$id limit 1");
    }
 
    function setUserName($id, $name)
    {
        $db = Factory::getDatabase('master');
        $db->query("update user set name = $name where id =$id limit 1");
    }
}

複製代碼

Extend/Factory.php

<?php
namespace Extend;
 
class Factory
{
    static function getDatabase($id)
    {
        $key = 'database_'.$id;
        if ($id == 'slave')
        {
            $slaves = Application::getInstance()->config['database']['slave'];
            $db_conf = $slaves[array_rand($slaves)];
        } else {
            $db_conf = Application::getInstance()->config['database'][$id];
        }
        //註冊樹模式存儲及獲取對象
        $db = Register::get($key);
        if (!$db) {
            $db = new Database\MySQLi();
            $db->connect($db_conf['host'], $db_conf['user'], $db_conf['password'], $db_conf['dbname']);
            Register::set($key, $db);
        }
        return $db;
    }
 
}

複製代碼

Extend/Application.php

<?php
namespace Extend;
 
class Application
{
    public $base_dir;
    protected static $instance;
 
    public $config;
 
    protected function __construct($base_dir)
    {
        $this->base_dir = $base_dir;
        $this->config = new Config($base_dir.'/configs');
    }
 
    static function getInstance($base_dir = '')
    {
        if (empty(self::$instance))
        {
            self::$instance = new self($base_dir);
        }
        return self::$instance;
    }
    
}

複製代碼

Extend/Config.php

<?php
namespace Extend;
 
/**
 * 配置類,繼承於php自帶的ArrayAccess接口
 * 容許一個對象以數組的方式訪問
 * Class Config
 * @package Extend
 */
class Config implements \ArrayAccess
{
    protected $path;
    protected $configs = array();
 
    function __construct($path)
    {
        $this->path = $path;
    }
 
    function offsetGet($key)
    {
        if (empty($this->configs[$key]))
        {
            $file_path = $this->path.'/'.$key.'.php';
            $config = require $file_path;
            $this->configs[$key] = $config;
        }
        return $this->configs[$key];
    }
 
    function offsetSet($key, $value)
    {
        throw new \Exception("cannot write config file.");
    }
 
    function offsetExists($key)
    {
        return isset($this->configs[$key]);
    }
 
    function offsetUnset($key)
    {
        unset($this->configs[$key]);
    }
}

複製代碼

configs/database.php

<?php
$config = array(
    'master' => array(
        'type' => 'MySQL',
        'host' => '127.0.0.1',
        'user' => 'root',
        'password' => '123456',
        'dbname' => 'test',
    ),
    'slave' => array(
        'slave1' => array(
            'type' => 'MySQL',
            'host' => '127.0.0.1',
            'user' => 'root',
            'password' => '123456',
            'dbname' => 'test',
        ),
        'slave2' => array(
            'type' => 'MySQL',
            'host' => '127.0.0.1',
            'user' => 'root',
            'password' => '123456',
            'dbname' => 'test',
        ),
    ),
);
return $config;

複製代碼

關於php代理模式文章 segmentfault.com/a/119000000…

5、其他設計模式以及總結

文章接:

segmentfault.com/a/119000001…

segmentfault.com/a/119000001…

6、面向對象編程的基本原則

一、單一職責原則:一個類只須要作好一件事情。不要使用一個類完成不少功能,而應該拆分紅更多更小的類。

二、開放封閉原則:一個類寫好以後,應該是可擴展而不可修改的。

三、依賴倒置原則:一個類不該該強依賴另一個類,每一個類對於另一個類都是可替換的。

四、配置化原則:儘可能使用配置,而不是硬編碼。

五、面向接口編程原則:只須要關心某個類提供了哪些接口,而不須要關心他的實現。

7、自動加載配置類文件

一、php中使用ArrayAccess實現配置文件的加載(使得程序能夠以數組的方式進行讀取配置)

(1)定義Config.php,繼承php自帶的ArrayAccess接口,並實現相應的方法,用於讀取和設置配置

Extend/Config.php

<?php
namespace Extend;
 
/**
 * 配置類,繼承於php自帶的ArrayAccess接口
 * 容許一個對象以數組的方式訪問
 * Class Config
 * @package Extend
 */
class Config implements \ArrayAccess
{
    protected $path;
    protected $configs = array();
 
    function __construct($path)
    {
        $this->path = $path;
    }
 
    function offsetGet($key)
    {
        if (empty($this->configs[$key]))
        {
            $file_path = $this->path.'/'.$key.'.php';
            $config = require $file_path;
            $this->configs[$key] = $config;
        }
        return $this->configs[$key];
    }
 
    function offsetSet($key, $value)
    {
        throw new \Exception("cannot write config file.");
    }
 
    function offsetExists($key)
    {
        return isset($this->configs[$key]);
    }
 
    function offsetUnset($key)
    {
        unset($this->configs[$key]);
    }
}

複製代碼

(2)configs/database.php

<?php
$config = array(
    'master' => array(
        'type' => 'MySQL',
        'host' => '127.0.0.1',
        'user' => 'root',
        'password' => '123456',
        'dbname' => 'test',
    ),
    'slave' => array(
        'slave1' => array(
            'type' => 'MySQL',
            'host' => '127.0.0.1',
            'user' => 'root',
            'password' => '123456',
            'dbname' => 'test',
        ),
        'slave2' => array(
            'type' => 'MySQL',
            'host' => '127.0.0.1',
            'user' => 'root',
            'password' => '123456',
            'dbname' => 'test',
        ),
    ),
);
return $config;

複製代碼

(3)讀取配置

<?php
 
$config = new Extend\Config(__DIR__.'/configs');
var_dump($config['database']);

複製代碼

到此,就能夠在程序中爲所欲爲的加載配置文件了。

二、在工廠方法中讀取配置,生成可配置化的對象

Extend/Factory.php

<?php
namespace Extend;
 
class Factory
{
    static function getDatabase($id)
    {
        $key = 'database_'.$id;
        if ($id == 'slave')
        {
            $slaves = Application::getInstance()->config['database']['slave'];
            $db_conf = $slaves[array_rand($slaves)];
        } else {
            $db_conf = Application::getInstance()->config['database'][$id];
        }
        //註冊樹模式存儲及獲取對象
        $db = Register::get($key);
        if (!$db) {
            $db = new Database\MySQLi();
            $db->connect($db_conf['host'], $db_conf['user'], $db_conf['password'], $db_conf['dbname']);
            Register::set($key, $db);
        }
        return $db;
    }
 
}

複製代碼

Extend/Application.php

<?php
namespace Extend;
 
class Application
{
    public $base_dir;
    protected static $instance;
 
    public $config;
 
    protected function __construct($base_dir)
    {
        $this->base_dir = $base_dir;
        $this->config = new Config($base_dir.'/configs');
    }
 
    static function getInstance($base_dir = '')
    {
        if (empty(self::$instance))
        {
            self::$instance = new self($base_dir);
        }
        return self::$instance;
    }
 
}

複製代碼

Extend/Config.php

<?php
namespace Extend;
 
/**
 * 配置類,繼承於php自帶的ArrayAccess接口
 * 容許一個對象以數組的方式訪問
 * Class Config
 * @package Extend
 */
class Config implements \ArrayAccess
{
    protected $path;
    protected $configs = array();
 
    function __construct($path)
    {
        $this->path = $path;
    }
 
    function offsetGet($key)
    {
        if (empty($this->configs[$key]))
        {
            $file_path = $this->path.'/'.$key.'.php';
            $config = require $file_path;
            $this->configs[$key] = $config;
        }
        return $this->configs[$key];
    }
 
    function offsetSet($key, $value)
    {
        throw new \Exception("cannot write config file.");
    }
 
    function offsetExists($key)
    {
        return isset($this->configs[$key]);
    }
 
    function offsetUnset($key)
    {
        unset($this->configs[$key]);
    }
}

複製代碼
相關文章
相關標籤/搜索