php精粹-编写高效的php代码 --- php设计模式
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<br/>'; } } class DBReadConnection extends DB { use Singleton; private function __construct() { echo 'DBReadConnection<br/>'; } } $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<br/>'; } } class DBReadConnection extends DBConnection { public function __construct() { echo 'DBReadConnection<br/>'; } } $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迭代器实现过滤
代码:
<?php 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迭代器
<?php // 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)
观察者模式的核心在于云霄你的应用程序注册一个回调,当某个特定的事件发生时便会促发它
代码示例:
<?php /** * 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 — php精粹-编写高效的php代码 --- API
1.数据格式 (1)json 示例代码: $jsonData = '[{"title":"The Magic Flute","time":1 ...
- 编写高效的Android代码
编写高效的Android代码 毫无疑问,基于Android平台的设备一定是嵌入式设备.现代的手持设备不仅仅是一部电话那么简单,它还是一个小型的手持电脑,但是,即使是最快的最高端的手持设备也远远比不上一 ...
- 编写高效的jQuery代码
http://www.css88.com/jqapi-1.9/ 编写高效的jQuery代码 最近写了很多的js,虽然效果都实现了,但是总感觉自己写的js在性能上还能有很大的提升.本文我计划总结一些网上 ...
- 如何在Android上编写高效的Java代码
转自:http://www.ituring.com.cn/article/177180 作者/ Erik Hellman Factor10咨询公司资深移动开发顾问,曾任索尼公司Android团队首席架 ...
- 如何编写高效的jQuery代码
jQuery的编写原则: 一.不要过度使用jQuery 1. jQuery速度再快,也无法与原生的javascript方法相比,而且建立的jQuery对象包含的信息量很庞大.所以有原生方法可以使用的场 ...
- 如何编写高效的jQuery代码(转载)
jQuery的编写原则: 一.不要过度使用jQuery 1. jQuery速度再快,也无法与原生的javascript方法相比,而且建立的jQuery对象包含的信息量很庞大.所以有原生方法可以使用的场 ...
- 利用on和off方法编写高效的js代码
先说下将这个话题的起因:最近发现公司的功能代码,很多在dom对象删除以后,其声明的绑定在window上的resize事件还一直存在,导致相同的功能代码执行了几次.对于我这种轻微代码洁癖的人来说,简直是 ...
- (转载)编写高效的jQuery代码
原文地址:http://www.cnblogs.com/ppforever/p/4084232.html 最近写了很多的js,虽然效果都实现了,但是总感觉自己写的js在性能上还能有很大的提升.本文我计 ...
- js学习笔记-编写高效、规范的js代码-Tom
编写高效.规范的js代码: 1.变量命名空间问题,尽量使用局部变量,防止命名冲突(污染作用域中的全局变量):全局空间命名的变量可以在对应的文档域任意位置中使用window调用. 2.尽量使用单var定 ...
随机推荐
- .NET程序集(Assembly)
在.NET 中,新引入了一个程序集的概念,就是指经由编译器编译得到的,供CLR进一步编译执行的那个中间产物,在WINDOWS系统中,它一般表现为.dll,或者是.exe的格式,但是要注意,它们跟普通意 ...
- xcode6 自定义UITabbarController
-(void)initTabBarView{ if (tabBarController && [kAPPDELEGATE.navigationController.viewContro ...
- Java中字符流与字节流的区别
字符流处理的单元为2个字节的Unicode字符,分别操作字符.字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组.所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单 ...
- select组件2
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...
- apache solr简单搭建
首先,下载位置是:http://lucene.apache.org/solr/downloads.html 官网的学习资料:http://lucene.apache.org/solr/quicksta ...
- 使用DrawerLayout实现侧拉菜单
侧拉菜单在android应用中非常常见,它的实现方式太多了,今天我们就说说使用Google提供的DrawerLayout来实现侧拉菜单效果,先来看张效果图: DrawerLayout的实现其实非常简单 ...
- TCP/IP协议原理与应用笔记14:电路交换 和 分组交换
1. 电路交换: (1)建立连接 (2)数据传输 (3)拆除连接 2. 分组交换 (1)数据报: 根据网络的特性,将数据报分成不同大小的部分,经过不同网路传递到相同的目的地.如下: 这里A--X 和 ...
- Android_ImageView
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools=&q ...
- find which process occupy the PORT
mac : lsof -i:8080 linux : netstat -anltp | grep 8080
- Linq to SQL只支持SQL Server(所选对象使用不支持的数据提供程序)