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设计模式的更多相关文章

  1. PHP — php精粹-编写高效的php代码 --- API

    1.数据格式 (1)json 示例代码: $jsonData = '[{"title":"The Magic Flute","time":1 ...

  2. 编写高效的Android代码

    编写高效的Android代码 毫无疑问,基于Android平台的设备一定是嵌入式设备.现代的手持设备不仅仅是一部电话那么简单,它还是一个小型的手持电脑,但是,即使是最快的最高端的手持设备也远远比不上一 ...

  3. 编写高效的jQuery代码

    http://www.css88.com/jqapi-1.9/ 编写高效的jQuery代码 最近写了很多的js,虽然效果都实现了,但是总感觉自己写的js在性能上还能有很大的提升.本文我计划总结一些网上 ...

  4. 如何在Android上编写高效的Java代码

    转自:http://www.ituring.com.cn/article/177180 作者/ Erik Hellman Factor10咨询公司资深移动开发顾问,曾任索尼公司Android团队首席架 ...

  5. 如何编写高效的jQuery代码

    jQuery的编写原则: 一.不要过度使用jQuery 1. jQuery速度再快,也无法与原生的javascript方法相比,而且建立的jQuery对象包含的信息量很庞大.所以有原生方法可以使用的场 ...

  6. 如何编写高效的jQuery代码(转载)

    jQuery的编写原则: 一.不要过度使用jQuery 1. jQuery速度再快,也无法与原生的javascript方法相比,而且建立的jQuery对象包含的信息量很庞大.所以有原生方法可以使用的场 ...

  7. 利用on和off方法编写高效的js代码

    先说下将这个话题的起因:最近发现公司的功能代码,很多在dom对象删除以后,其声明的绑定在window上的resize事件还一直存在,导致相同的功能代码执行了几次.对于我这种轻微代码洁癖的人来说,简直是 ...

  8. (转载)编写高效的jQuery代码

    原文地址:http://www.cnblogs.com/ppforever/p/4084232.html 最近写了很多的js,虽然效果都实现了,但是总感觉自己写的js在性能上还能有很大的提升.本文我计 ...

  9. js学习笔记-编写高效、规范的js代码-Tom

    编写高效.规范的js代码: 1.变量命名空间问题,尽量使用局部变量,防止命名冲突(污染作用域中的全局变量):全局空间命名的变量可以在对应的文档域任意位置中使用window调用. 2.尽量使用单var定 ...

随机推荐

  1. android中利用实现二级联动的效果

    按照惯例,首先上一张效果图. 本篇文章实现的效果就是如图中所圈的那样,实现类似于HTML中的二级联动的效果. 对于第一个选项我们读取的是本地xml文件来填充数据的, 对于第二个选项我们读取的是通过中央 ...

  2. [Canvas] Introduction to drawing with p5js

    In this lesson we look at using color and the basic drawing methods of p5js, and how they can be com ...

  3. linux下启动某个进程

    在关闭窗口的情况下,能够在后台继续运行,如 启动命令 node /home/node_modules/pixel-ping/lib/pixel-ping.js /home/node_modules/p ...

  4. 各种会义PPT

    http://vdisk.weibo.com/s/dBzv2siaHK2H http://vdisk.weibo.com/wap/u/3460619722 https://yunqi.aliyun.c ...

  5. 【转】Cocos2d-x 2.x CCSprite 灰白图的生成(利用shader设置)——2013-08-27 21

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=325 游戏中人物死掉后要把人物头 ...

  6. Java常见异常总结

    算术异常类:ArithmeticExecption 空指针异常类:NullPointerException 类型强制转换异常:ClassCastException 数组负下标异常:NegativeAr ...

  7. Objective-c中autorelease的释放时机

    如果你使用过MRR,autorelease这个关键字应该是太熟悉了,每次在我们生成一个新的对象返回时,都需要向这个对象发送autorelease消息,目的是为了延时释放创建的对象.那到底是在什么时候, ...

  8. 推荐几个对Asp.Net开发者比较实用的工具 2

    推荐几个对Asp.Net开发者比较实用的工具.大家有相关工具也可以在评论区留言,一起努力学习. 作为程序员要有挑战精神,大家可以尝试一下这些工具. 已经有篇文章写到了vs的扩展工具,这里不再累赘,请查 ...

  9. sql 判断表是否存在

    if object_id(N'tablename',N'U') is not nulldrop table tablenamego if exists (select * from sysobject ...

  10. Atom 插件安装

    “webstom” 目前还没免费版的 不过类似的倒是有个! 首先想到的一句话是:还在为webstom不是正版而发愁吗? 其实很小的时候我们的世界是非黑即白的,但慢慢长大后,发现其实还有灰色的存在! 工 ...