1,選擇一個最合適的設計模式 沒有任何事物是完美的,也沒有人說過設計模式一個嚴格的放之四海而皆準的解決方案。因此你可以改變這些模式,使它們更適合手頭的工作。對於某些設計模式而言,他們就是所屬程式固有的天性;而對於其他的一些設計模式,你可以改變其自身的模式。模式之間互相配合、協同工作已經很常見。它們構成了整個應用(至少一部分)的基礎。 2.單例模式
// The Database class represents our global DB connection
- class Database{
- // A static variable to hold our single instance
- private static $_instance = null;
-
- // Make the constructor private to ensure singleton
- private function __construct()
- {
- echo 'constructor';
- }
// A method to get our singleton instance
- public static function getInstance()
- {
- if (!(self::$_instance instanceof Database)) {
- self::$_instance = new Database();
- }
-
- return self::$_instance;
- }
- }
$database = Database::getInstance();
- var_dump($database);
複製代碼問題:使用單例模式不能建立兩個執行個體,可用Traits解決建立兩個不同類型的執行個體的問題,但仍然不能解決建立兩個相同執行個體的問題(可用註冊表模式解決)。 建立兩個不同類的執行個體 代碼:
- trait Singleton {
- private static $_instance = null;
-
- public static function getInstance() {
- $class = __CLASS__;
-
- if(!(self::$_instance instanceof $class)) {
- self::$_instance = new $class;
- }
-
- return self::$_instance;
- }
-
- }
- class DB {
- }
-
- class DBWriteConnection extends DB {
- use Singleton;
-
- private function __construct() {
- echo 'DBWriteConnection
';
- }
- }
-
- class DBReadConnection extends DB {
- use Singleton;
-
- private function __construct() {
- echo 'DBReadConnection
';
- }
- }
-
- $dbWriteConnection = DBWriteConnection::getInstance();
- var_dump($dbWriteConnection);
複製代碼3.註冊表模式註冊表模式僅僅是一個單獨的全域類,在你需要的時候允許代碼檢索一個對象的相同執行個體,也可以在你需要的時建立另一個執行個體(一經要求將再次訪問那些全域執行個體)。 Registry類:
class Registry {
- /**
- * @var array The store for all of our objects
- */
- static private $_store = array();
-
- /**
- * Add an object to the registry
- *
- * If you do not specify a name the classname is used
- *
- * @param mixed $object The object to store
- * @param string $name Name used to retrieve the object
- * @return void
- * @throws Exception
- */
- static public function add($object, $name = null)
- {
- // Use the classname if no name given, simulates singleton
- $name = (!is_null($name)) ?$name:get_class($object);
- if (isset(self::$_store[$name])) {
- throw new Exception("Object already exists in registry");
- }
-
- self::$_store[$name]= $object;
- }
-
- /**
- * Get an object from the registry
- *
- * @param string $name Object name, {@see self::set()}
- * @return mixed
- * @throws Exception
- */
- static public function get($name)
- {
- if (!self::contains($name)) {
- throw new Exception("Object does not exist in registry");
- }
return self::$_store[$name];
- }
-
- /**
- * Check if an object is in the registry
- *
- * @param string $name Object name, {@see self::set()}
- * @return bool
- */
- static public function contains($name)
- {
- if (!isset(self::$_store[$name])) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Remove an object from the registry
- *
- * @param string $name Object name, {@see self::set()}
- * @returns void
- */
- static public function remove($name)
- {
- if (self::contains($name)) {
- unset(self::$_store[$name]);
- }
- }
- }
-
複製代碼在類外部,使用Registry類:
require 'Registry.php';
class DBReadConnection {}
- class DBWriteConnection {}
$read = new DBReadConnection;
- Registry::add($read);
$write = new DBWriteConnection;
- Registry::add($write);
// To get the instances, anywhere in our code:
- $read = Registry::get('DBReadConnection');
- $write = Registry::get('DBWriteConnection');
var_dump($read);
- var_dump($write);
-
複製代碼在類內部使用Registry表類,使用者不與Registry互動。 範例程式碼:
require 'Registry.php';
abstract class DBConnection {
- static public function getInstance($name = null)
- {
- // Get the late-static-binding version of __CLASS__
- $class = get_called_class();
- // Allow passing in a name to get multiple instances
- // If you do not pass a name, it functions as a singleton
- $name = (!is_null($name)) ? $name:$class;
- if (!Registry::contains($name)) {
- $instance = new $class();
- Registry::add($instance, $name);
- }
- return Registry::get($name);
- }
- }
class DBWriteConnection extends DBConnection {
- public function __construct()
- {
- echo 'DBWriteConnection
';
- }
- }
class DBReadConnection extends DBConnection {
- public function __construct()
- {
- echo 'DBReadConnection
';
- }
- }
$dbWriteConnection = DBWriteConnection::getInstance('abc');
- var_dump($dbWriteConnection);
- $dbReadConnection = DBReadConnection::getInstance();
- var_dump($dbReadConnection);
複製代碼4.原廠模式工廠(factory)模式製造對象,就像工業界與它同名的鋼筋混泥土行業一樣。通常,我們將原廠模式用於初始化相同抽象類別或者介面的具體實現。 在通常方式下,雖然人們極少採用原廠模式,但是它仍是最適合初始化基於驅動安裝的許多變種的一種。例如不同的配置、會話或緩衝儲存引擎。原廠模式的最大價值在於它可以將多個對象設定封裝成單一、簡單的方法調用。
- /**
- * Log Factory
- *
- * Setup and return a file, mysql, or sqlite logger
- */
- class Log_Factory {
- /**
- * Get a log object
- *
- * @param string $type The type of logging backend, file, mysql or sqlite
- * @param array $options Log class options
- */
- public function getLog($type = 'file', array $options)
- {
- // Normalize the type to lowercase
- $type = strtolower($type);
-
- // Figure out the class name and include it
- $class = "Log_" .ucfirst($type);
- require_once str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
-
- // Instantiate the class and set the appropriate options
- $log = new $class($options);
- switch ($type) {
- case 'file':
- $log->setPath($options['location']);
- break;
- case 'mysql':
- $log->setUser($options['username']);
- $log->setPassword($options['password']);
- $log->setDBName($options['location']);
- break;
- case 'sqlite':
- $log->setDBPath($otions['location']);
- break;
- }
-
- return $log;
- }
- }
複製代碼5.迭代模式迭代模式允許我們將foreach的效能添加到任何對象的內部儲存資料,而不僅僅添加到公用屬性。它覆蓋了預設的foreach行為,並允許我們為迴圈注入商務邏輯。 (1)使用Iterator迭代器介面
class BasicIterator implements Iterator {
- private $key = 0;
- private $data = array(
- "hello",
- "world",
- );
public function __construct() {
- $this->key = 0;
- }
public function rewind() {
- $this->key = 0;
- }
public function current() {
- return $this->data[$this->key];
- }
public function key() {
- return $this->key;
- }
public function next() {
- $this->key++;
- return true;
- }
public function valid() {
- return isset($this->data[$this->key]);
- }
- }
$iterator = new BasicIterator();
- $iterator->rewind();
do {
- $key = $iterator->key();
- $value = $iterator->current();
- echo $key .': ' .$value . PHP_EOL;
- } while ($iterator->next() && $iterator->valid());
- $iterator = new BasicIterator();
- foreach ($iterator as $key => $value) {
- echo $key .': ' .$value . PHP_EOL;
- }
複製代碼(2)使用RecursiveIteratorIterator迭代器遍曆數組
$array = array(
- "Hello", // Level 1
- array(
- "World" // Level 2
- ),
- array(
- "How", // Level 2
- array(
- "are", // Level 3
- "you" // Level 3
- )
- ),
- "doing?" // Level 1
- );
$recursiveIterator = new RecursiveArrayIterator($array);
$recursiveIteratorIterator = new RecursiveIteratorIterator($recursiveIterator);
foreach ($recursiveIteratorIterator as $key => $value) {
- echo "Depth: " . $recursiveIteratorIterator->getDepth() . PHP_EOL;
- echo "Key: " . $key . PHP_EOL;
- echo "Value: " .$value . PHP_EOL;
- }
複製代碼 (3)用FilterIterator迭代器實現過濾
class EvenFilterIterator extends FilterIterator {
- /**
- * Accept only even-keyed values
- *
- * @return bool
- */
- public function accept()
- {
- // Get the actual iterator
- $iterator = $this->getInnerIterator();
-
- // Get the current key
- $key = $iterator->key();
-
- // Check for even keys
- if ($key % 2 == 0) {
- return true;
- }
-
- return false;
- }
- }
$array = array(
- 0 => "Hello",
- 1 => "Everybody Is",
- 2 => "I'm",
- 3 => "Amazing",
- 4 => "The",
- 5 => "Who",
- 6 => "Doctor",
- 7 => "Lives"
- );
// Create an iterator from our array
- $iterator = new ArrayIterator($array);
// Create our FilterIterator
- $filterIterator = new EvenFilterIterator($iterator);
// Iterate
- foreach ($filterIterator as $key => $value) {
- echo $key .': '. $value . PHP_EOL;
- }
- ?>
複製代碼(4)RegexIterator迭代器
// Create a RecursiveDirectoryIterator
- $directoryIterator = new RecursiveDirectoryIterator("./");
// Create a RecursiveIteratorIterator to recursively iterate
- $recursiveIterator = new RecursiveIteratorIterator($directoryIterator);
// Createa filter for *Iterator*.php files
- $regexFilter = new RegexIterator($recursiveIterator, '/(.*?)Iterator(.*?)\.php$/');
// Iterate
- foreach ($regexFilter as $key => $file) {
- /* @var SplFileInfo $file */
- echo $file->getFilename() . PHP_EOL;
- }
-
複製代碼功能:找到所有的php檔案 (4)LimitItertor迭代器,像SQL中的LIMIT
// Define the array
- $array = array(
- 'Hello',
- 'World',
- 'How',
- 'are',
- 'you',
- 'doing?'
- );
// Create the iterator
- $iterator = new ArrayIterator($array);
// Create the limiting iterator, to get the first 2 elements
- $limitIterator = new LimitIterator($iterator, 0, 2);
// Iterate
- foreach ($limitIterator as $key => $value) {
- echo $key .': '. $value . PHP_EOL;
- }
複製代碼6.觀察者模式(observer) 觀察者模式的核心在於雲霄你的應用程式註冊一個回調,當某個特定的事件發生時便會促發它
/**
- * The Event Class
- *
- * With this class you can register callbacks that will
- * be called (FIFO) for a given event.
- */
- class Event {
- /**
- * @var array A multi-dimentional array of events => callbacks
- */
- static protected $callbacks = array();
-
- /**
- * Register a callback
- *
- * @param string $eventName Name of the triggering event
- * @param mixed $callback An instance of Event_Callback or a Closure
- */
- static public function registerCallback($eventName, $callback)
- {
- if (!($callback instanceof Event_Callback) && !($callback instanceof Closure)) {
- throw new Exception("Invalid callback!");
- }
-
- $eventName = strtolower($eventName);
-
- self::$callbacks[$eventName][] = $callback;
- }
-
- /**
- * Trigger an event
- *
- * @param string $eventName Name of the event to be triggered
- * @param mixed $data The data to be sent to the callback
- */
- static public function trigger($eventName, $data)
- {
- $eventName = strtolower($eventName);
-
- if (isset(self::$callbacks[$eventName])) {
- foreach (self::$callbacks[$eventName] as $callback) {
- self::callback($callback, $data);
- }
- }
- }
-
- /**
- * Perform the callback
- *
- * @param mixed $callback An instance of Event_Callback or a Closure
- * @param mixed $data The data sent to the callback
- */
- static protected function callback($callback, $data)
- {
- if ($callback instanceof Closure) {
- $callback($data);
- } else {
- $callback->run($data);
- }
- }
- }
/**
- * The Event Callback interface
- *
- * If you do not wish to use a closure
- * you can define a class that extends
- * this instead. The run method will be
- * called when the event is triggered.
- */
- interface Event_Callback {
- public function run($data);
- }
/**
- * Logger callback
- */
- class LogCallback implements Event_Callback {
- public function run($data)
- {
- echo "Log Data" . PHP_EOL;
- var_dump($data);
- }
- }
// Register the log callback
- Event::registerCallback('save', new LogCallback());
// Register the clear cache callback as a closure
- Event::registerCallback('save', function ($data) {
- echo "Clear Cache" . PHP_EOL;
- var_dump($data);
- });
class MyDataRecord {
- public function save()
- {
- // Save data
-
- // Trigger the save event
- Event::trigger('save', array("Hello", "World"));
- }
- }
// Instantiate a new data record
- $data = new MyDataRecord();
- $data->save(); // 'save' Event is triggered here
複製代碼7.依賴注入模式依賴注入模式允許類的使用這為這個類注入依賴的行為。
/**
- * Log Class
- */
- class Log {
- /**
- * @var Log_Engine_Interface
- */
- protected $engine = false;
-
- /**
- * Add an event to the log
- *
- * @param string $message
- */
- public function add($message)
- {
- if (!$this->engine) {
- throw new Exception('Unable to write log. No Engine set.');
- }
-
- $data['datetime'] = time();
- $data['message'] = $message;
-
- $session = Registry::get('session');
- $data['user'] = $session->getUserId();
-
- $this->engine->add($data);
- }
-
- /**
- * Set the log data storage engine
- *
- * @param Log_Engine_Interface $Engine
- */
- public function setEngine(Log_Engine_Interface $engine)
- {
- $this->engine = $engine;
- }
-
- /**
- * Retrieve the data storage engine
- *
- * @return Log_Engine_Interface
- */
- public function getEngine()
- {
- return $this->engine;
- }
- }
interface Log_Engine_Interface {
- /**
- * Add an event to the log
- *
- * @param string $message
- */
- public function add(array $data);
- }
class Log_Engine_File implements Log_Engine_Interface {
- /**
- * Add an event to the log
- *
- * @param string $message
- */
- public function add(array $data)
- {
- $line = '[' .data('r', $data['datetime']). '] ' .$data['message']. ' User: ' .$data['user'] . PHP_EOL;
-
- $config = Registry::get('site-config');
-
- if (!file_put_contents($config['location'], $line, FILE_APPEND)) {
- throw new Exception("An error occurred writing to file.");
- }
- }
- }
$engine = new Log_Engine_File();
$log = new Log();
- $log->setEngine($engine);
// Add it to the registry
- Registry::add($log);
複製代碼依賴注入不想原廠模式,日之類無需瞭解每一個不同的儲存引擎的相關知識。這就意味著任何使用日誌類的開發人員可以添加他們自己的儲存引擎,主要他們複合介面就行。 8.模型-視圖-控制器模型-視圖-控制器又稱為MVC模式,是描述應用程式3個不同層次之間關係的一種方式。模型-資料層 所有的輸出資料都來自模型。它可能是一個資料庫、web服務或者檔案。視圖-表現層 負責將資料從模型中取出並輸出給使用者。控制器-應用程式流程層 根據使用者的請求調用相應的模型檢索出請求的資料,然後調用視圖將操作的結果顯示給使用者。 一個典型的MVC架構圖: 9.對模式的理解 模式是很多常見問題的最佳解決方案。 您可能感興趣的文章:
- php設計模式之單例模式、原廠模式與觀察者模式
- 深入php設計模式執行個體詳解
- php設計模式執行個體之命令模式
- php設計模式執行個體之觀察者模式(2)
- PHP設計模式執行個體之觀察者模式
- php設計模式執行個體之原廠模式
- php設計模式執行個體之單例模式
- PHP設計模式之觀察者模式的例子
- php設計模式之原廠模式的執行個體代碼
- php設計模式之單例模式的執行個體代碼
- php常用設計模式之原廠模式與單例模式介紹
- 學習php設計模式之單例模式
- php常用的三種設計模式的學習筆記
- php設計模式之單例模式學習
|