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定 ...
随机推荐
- Test-NetConnection
有了PowerShell 4.0,排除网络故障的日子就会轻松很多.Test-NetConnection cmdlet将多个测试整合到了一个简单的有几个参数的命令当中.例如,命令Test-NetConn ...
- sed命令详解及应用实例
第一部分:Sed基本用法 sed是非交互式的编辑器.它不会修改文件,除非使用shell重定向来保存结果.默认情况下,所有的输出行都被打印到屏幕上. sed编辑器逐行处理文件(或输入),并将结果发送到屏 ...
- 多项式逼近remes算法
http://wenku.baidu.com/link?url=gpaBIucx0ov0ez3QHrO4FooBtNz2i80s4LKsh-LV3NnPYNjTUu7e1V7bT_jMHwOUZk4X ...
- java查询手机号码归属地
package com; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamRe ...
- APT源
Debian 6.0.7 deb http://mirrors.163.com/debian squeeze main non-free contribdeb http://mirrors.163.c ...
- linux服务器下tomcat部署项目内存溢出
今天将一个项目部署到linux服务器上,结果tomcat在启动运行到一定时间后就报错.根据以往的经验,查了一些资料,终于解决了该问题并顺便解决了生产环境中的get方式中文传递乱码问题. tomcat启 ...
- apache性能配置优化
最近在进行apache性能优化设置.在修改apache配置文件之前需要备份原有的配置文件夹conf,这是网站架设的好习惯.以下的apache配置调优均是在red had的环境下进行的. httpd相关 ...
- Fragment碎片频繁来回切换的时候报java.lang.IllegalStateException: No activity
出现这个问题的原因是因为使用的transcation.replace(fragmentTwo);的方式进行碎片切换的. 解决方案是使用add和show.hide方法组合实现碎片的切换(应该是显示.隐藏 ...
- sea.js 学习
开篇:终于学习了sea.js的使用了,因为它是一个模块加载工具,所以首先要了解javascript的模块编程,然后对sea.js的了解和使用 javascript 模块编程 为什么要模块化编程,为了让 ...
- android 6.0特性翻译 --渣渣
所有关于Android 6.0 棉花糖的知识 上下文帮助 1.现在按压:不需要离开你正在运行的app或者访问的网站就可 获取帮助,仅仅触摸和按下Home按钮.(长按Home键,可以在 android ...