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. struts2设置<s:select>默认选中项的方法

    struts2的select标签中,常用的有以下几个属性:(1)struts2中的select 标签中,必须设置的属性只有一个,即是list.(2)select标签的list中必须有值,不然会报错.如 ...

  2. 关于css中z-index 的应用

    我想很多人在应用中的会碰到这个问题,设置 z-index无效:无论设置为多高的数字都没有效果: 原因是在设置z-index之前必须满足一下两个条件: 1,给设置z-index的元素设置相应的定位值,p ...

  3. MediaProvider 数据库Audio相关表格相应关系

    Android的MediaProvider提供了一系列的Audio,Video,image等表格,用于APP检索相关的音视频图片信息. 首先来看看Audio相关的表格: ×图中颜色同样的部分表示来自于 ...

  4. JSON未定义解决办法

    json官网:www.json.org json2.js地址:https://github.com/douglascrockford/JSON-js/blob/master/json2.js 引入解决 ...

  5. 常用加密算法的Java实现(一) ——单向加密算法MD5和SHA

    1.Java的安全体系架构 1.1           Java的安全体系架构介绍 Java中为安全框架提供类和接口.JDK 安全 API 是 Java 编程语言的核心 API,位于 java.sec ...

  6. android datepicker源码

    /* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Versi ...

  7. DW一些快捷键的使用

    在键盘上敲空格的话可以使用shift+空格 如果要换行的话就可以使用的是 shift+enter

  8. Android_常用控件及适配器

    TextView 控件中显示的内容必须是文本 TextView中常用的属性 android:text TextView中显示的文本内容 android:textColor 字体颜色 格式为#RGB # ...

  9. 用ModelSim仿真SDRAM操作

    之前写了两篇关于Modelsim仿真的blog,其中模块管脚的命名可能让人觉得有些奇怪,其实不然,之前的两篇内容都是为了仿真SDRAM操作做铺垫的. 由于SDRAM的仿真过程相对比较复杂,也比较繁琐. ...

  10. 深入Windows窗体原理及控件重绘技巧

    之前有学MFC的同学告诉我觉得Windows的控件重绘难以理解,就算重绘成功了还是有些地方不明白,我觉得可能很多人都有这样的问题,在这里我从Windows窗体的最基本原理来讲解,如果你有类似的疑惑希望 ...