PHP 常见设计模式及示例

在这里插入图片描述

1.单例模式

单例模式顾名思义,就是只有一个实例。作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

单例模式的要点有三个:

  • 一是某个类只能有一个实例;
  • 二是它必须自行创建这个实例;
  • 三是它必须自行向整个系统提供这个实例。

为什么要使用PHP单例模式

  1. php的应用主要在于数据库应用, 一个应用中会存在大量的数据库操作, 在使用面向对象的方式开发时, 如果使用单例模式, 则可以避免大量的new 操作消耗的资源,还可以减少数据库连接这样就不容易出现 too many connections情况。
  2. 如果系统中需要有一个类来全局控制某些配置信息, 那么使用单例模式可以很方便的实现. 这个可以参看zend Framework的FrontController部分。
  3. 在一次页面请求中, 便于进行调试, 因为所有的代码(例如数据库操作类db)都集中在一个类中, 我们可以在类中设置钩子, 输出日志,从而避免到处var_dump, echo。

例子:

控制层使用
...

use app\index\model\Users as UsersModel;

...

/**
* 添加一条数据
*/
public function add(){
    $u_id = intval(input('id'));
    $u_name = input('name');
    $u_age = 18;
    $u_sex = 0;
    $insertOne = UsersModel::insertOne($u_id,$u_name,$u_age,$u_sex);
    if($insertOne){
        $this->success("插入".$u_name."成功");
    }
    else{
        $this->error("插入".$u_name."失败");
    }
}

model层
<?php
namespace app\index\model;

use think\Model;
use think\Db;
/**
 * 使用静态方法 static 而不是 public 在controller里面不用new 直接用 会方便很多
 */
class Users extends Model
{
    private static $instance;
    protected $defaultField = 'danli';
    private function __clone(){} //禁止被克隆
    /**
    * 单例
    */
    public static function getInstance()
    {
        if(!(self::$instance instanceof self)){
            self::$instance = new static();
        }
        return self::$instance;
    }
    /**
    * 添加一条数据
    */
    public static function insertOne($uid,$uname,$uage,$usex)
    {
          $inserttheone = self::getInstance()->execute("insert into users(u_id,u_name,u_age,u_sex) value(".$uid.",'".$uname."',".$uage.",".$usex.")");
          if($inserttheone)
          {
          	return true;
          }
          else{
          	return false;
          }
    }
  /**
  * 删除一条数据
  */
  public static function deleteOne($uid){
    $delone = self::getInstance()->execute("delete from users where u_id = ".$uid."");
    if($delone){
    	return true;
    }else{
   	 	return false;
    }
  }
  /**
  *  修改一条数据
  */
  public static function updateOne($uid,$age){
    $updateone = self::getInstance()->execute("update users set u_age = ".$age." where u_uid = ".$uid."");
    if($updateone){
    	return true;
    }else{
    	return false;
    }
  }
  /**
  * 查询
  */
  public static function query($defaultField,$uid){
  	if($defaultField == '' || empty($defaultField) || is_null($defaultField)){
  		$defaultField = '*';
  	}
    if($uid == '' || empty($uid) || is_null($uid)){
      $uid = '';
    }else{
      $uid = "where u_id = $uid";
    }
    return self::getInstance()->query("select $defaultField from users $uid");

  }
  /**
  * 批量修改
  */
  public static function batchUpdate($arr)
  {
    foreach ($arr as $key => $value) {
      $updatearr = self::getInstance()->execute("update users set u_name = '".$value['u_name']."',u_age = ".$value['u_age'].",u_sex = ".$value['u_sex']." where u_uid = ".$uid."");
      if($updatearr){
           return true;
      }else{
         return false;
      }
      }
    }
  }

/**
* 设计模式之单例模式
* $_instance必须声明为静态的私有变量
* 构造函数必须声明为私有,防止外部程序new类从而失去单例模式的意义
* getInstance()方法必须设置为公有的,必须调用此方法以返回实例的一个引用
* ::操作符只能访问静态变量和静态函数
* new对象都会消耗内存
* 使用场景:最常用的地方是数据库连接。
* 使用单例模式生成一个对象后,该对象可以被其它众多对象所使用。
*/
class man
{
//保存例实例在此属性中
private static $_instance;

//构造函数声明为private,防止直接创建对象
private function __construct()
{
echo '我被实例化了!';
}

//单例方法
public static function get_instance()
{
  var_dump(isset(self::$_instance));

  if(!isset(self::$_instance))
  {
  self::$_instance=new self();
  }
  return self::$_instance;
}

//阻止用户复制对象实例
private function __clone()
{
	trigger_error('Clone is not allow' ,E_USER_ERROR);
}

function test()
{
	echo("test");
}
}

// 这个写法会出错,因为构造方法被声明为private
//$test = new man;

// 下面将得到Example类的单例对象
$test = man::get_instance();
$test = man::get_instance();
$test->test();

// 复制对象将导致一个E_USER_ERROR.
//$test_clone = clone $test;

2.简单工厂模式


①抽象基类:类中定义抽象一些方法,用以在子类中实现
②继承自抽象基类的子类:实现基类中的抽象方法
③工厂类:用以实例化所有相对应的子类

/**
*
* 定义个抽象的类,让子类去继承实现它
*
*/
abstract class Operation{
//抽象方法不能包含函数体
abstract public function getValue($num1,$num2);//强烈要求子类必须实现该功能函数
}

/**
* 加法类
*/
class OperationAdd extends Operation {
  public function getValue($num1,$num2){
  return $num1+$num2;
  }
}
/**
* 减法类
*/
class OperationSub extends Operation {
  public function getValue($num1,$num2){
  return $num1-$num2;
  }
}
/**
* 乘法类
*/
class OperationMul extends Operation {
  public function getValue($num1,$num2){
  return $num1*$num2;
  }
}
/**
* 除法类
*/
class OperationDiv extends Operation {
  public function getValue($num1,$num2){
    try {
      if ($num2==0){
      	throw new Exception("除数不能为0");
      }else {
      	return $num1/$num2;
      }
    }catch (Exception $e){
    	echo "错误信息:".$e->getMessage();
    }
  }
}

通过采用面向对象的继承特性,我们可以很容易就能对原有程序进行扩展,比如:‘乘方’,‘开方’,‘对数’,‘三角函数’,‘统计’等,以还可以避免加载没有必要的代码。

如果我们现在需要增加一个求余的类,会非常的简单

我们只需要另外写一个类(该类继承虚拟基类),在类中完成相应的功能(比如:求乘方的运算),而且大大的降低了耦合度,方便日后的维护及扩展

/**
* 求余类(remainder)
*
*/
class OperationRem extends Operation {
  public function getValue($num1,$num2){
  return $num1%$num12;
  }
}

现在还有一个问题未解决,就是如何让程序根据用户输入的操作符实例化相应的对象呢?
解决办法:使用一个单独的类来实现实例化的过程,这个类就是工厂

/**
* 工程类,主要用来创建对象
* 功能:根据输入的运算符号,工厂就能实例化出合适的对象
*
*/
class Factory{
  public static function createObj($operate){
    switch ($operate){
      case '+':
        return new OperationAdd();
        break;
      case '-':
        return new OperationSub();
        break;
      case '*':
        return new OperationSub();
        break;
      case '/':
        return new OperationDiv();
        break;
      }
    }
  }
$test=Factory::createObj('/');
$result=$test->getValue(23,0);
echo $result;

其他关于关于此模式的笔记:

工厂模式:

以交通工具为例子:要求请既可以定制交通工具,又可以定制交通工具生产的过程

1 > 定制交通工具

  • 1.定义一个接口,里面包含交工工具的方法(启动 运行 停止)
  • 2.让飞机,汽车等类去实现他们

2 > 定制工厂(通上类似)

  • 1.定义一个接口,里面包含交工工具的制造方法(启动 运行 停止)
  • 2.分别写制造飞机,汽车的工厂类去继承实现这个接口

demo

简单工厂模式

介绍:
简单工厂模式又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类

简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。

<?php
//简单工厂模式
class Cat{
  function __construct(){
      echo "I am Cat class <br>";
	}
}
class Dog{
	function __construct(){
      echo "I am Dog class <br>";
  }
}
class Factory{
  public static function CreateAnimal($name){
    if ($name == 'cat') {
    	return new Cat();
    } elseif ($name == 'dog') {
    	return new Dog();
    }
  }
}

$cat = Factory::CreateAnimal('cat');
$dog = Factory::CreateAnimal('dog');
工厂方法模式

工厂方法模式是简单工厂模式的进一步抽象和推广。

由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。

在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。

这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。

<?php
interface Animal{
  public function run();
  public function say();
}

class Cat implements Animal{
    public function run(){
        echo "I ran slowly <br>";
    }
    public function say(){
      echo "I am Cat class <br>";
    }
}

class Dog implements Animal{
    public function run(){
      echo "I'm running fast <br>";
    }
    public function say(){
      echo "I am Dog class <br>";
    }
}
  
abstract class Factory{
  	abstract static function createAnimal();
}
  
class CatFactory extends Factory{
  	public static function createAnimal(){
    	return new Cat();
    }
}
  
class DogFactory extends Factory{
    public static function createAnimal(){
    	return new Dog();
    }
}

$cat = CatFactory::createAnimal();
$cat->say();
$cat->run();

$dog = DogFactory::createAnimal();
$dog->say();
$dog->run();
抽象工厂模式

抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。

此模式是对工厂方法模式的进一步扩展。在工厂方法模式中,一个具体的工厂负责生产一类具体的产品,即一对一的关系,但是,如果需要一个具体的工厂生产多种产品对象,那么就需要用到抽象工厂模式了。

<?php

interface TV{
  public function open();
  public function use();
}

class HaierTv implements TV{
  public function open()
  {
      echo "Open Haier TV <br>";
  }

  public function use(){
    echo "I'm watching TV <br>";
  }
}

interface PC{
  public function work();
  public function play();
}

class LenovoPc implements PC
{
  public function work()
  {
  	echo "I'm working on a Lenovo computer <br>";
  }
  public function play()
  {
  	echo "Lenovo computers can be used to play games <br>";
  }
}

abstract class Factory{
  abstract public static function createPc();
  abstract public static function createTv();
}

class ProductFactory extends Factory
{
    public static function createTV(){
    	return new HaierTv();
    }
    public static function createPc(){
      return new LenovoPc();
    }
}

$newTv = ProductFactory::createTV();
$newTv->open();
$newTv->use();

$newPc = ProductFactory::createPc();
$newPc->work();
$newPc->play();

3.观察者模式

观察者模式属于行为模式,是定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。它完美的将观察者对象和被观察者对象分离。可以在独立的对象(主体)中维护一个对主体感兴的依赖项(观察器)列表。 让所有观察器各自实现公共的 Observer接口,以取消主体和依赖性对象之间的直接依赖关系。(反正我看不明白)

用到了 spl (standard php library)

class MyObserver1 implements SplObserver {
  public function update(SplSubject $subject) {
      echo __CLASS__ . ' - ' . $subject->getName();
    }
}

class MyObserver2 implements SplObserver {
  public function update(SplSubject $subject) {
  	echo __CLASS__ . ' - ' . $subject->getName();
  }
}

class MySubject implements SplSubject {
  private $_observers;
  private $_name;

  public function __construct($name) {
    $this->_observers = new SplObjectStorage();
    $this->_name = $name;
  }

  public function attach(SplObserver $observer) {
  	$this->_observers->attach($observer);
  }

  public function detach(SplObserver $observer) {
  	$this->_observers->detach($observer);
  }

  public function notify() {
    foreach ($this->_observers as $observer) {
    	$observer->update($this);
    }
  }

  public function getName() {
    return $this->_name;
    }
  }

  $observer1 = new MyObserver1();
  $observer2 = new MyObserver2();

  $subject = new MySubject("test");

  $subject->attach($observer1);
  $subject->attach($observer2);

  $subject->notify();

参考原文:http://www.php.net/manual/zh/class.splsubject.php

4.策略模式

在此模式中,算法是从复杂类提取的,因而可以方便地替换。例如,如果要更改搜索引擎中排列页的方法,则策略模式是一个不错的选择。思考一下搜索引擎的几个部分 —— 一部分遍历页面,一部分对每页排列,另一部分基于排列的结果排序。在复杂的示例中,这些部分都在同一个类中。通过使用策略模式,您可将排列部分放入另一个类中,以便更改页排列的方式,而不影响搜索引擎的其余代码。

作为一个较简单的示例,下面 显示了一个用户列表类,它提供了一个根据一组即插即用的策略查找一组用户的方法

策略模式,生活中的实例:

例如下班回家,我可以有几个策略考虑:我可以骑单车,乘坐公交车,乘地铁等策略

还比如网站开发个人中心页面,不同的用户类型显示个人主页不一样,这个可以使用策略模式,即针对不同的用户使用不同的策略。

//定义接口
interface IStrategy {
	function filter($record);
}

//实现接口方式1
class FindAfterStrategy implements IStrategy {
  private $_name;
  public function __construct($name) {
  $this->_name = $name;
}
public function filter($record) {
	return strcmp ( $this->_name, $record ) <= 0;
}
}

//实现接口方式1
class RandomStrategy implements IStrategy {
  public function filter($record) {
  	return rand ( 0, 1 ) >= 0.5;
  }
}

//主类
class UserList {
private $_list = array ();
public function __construct($names) {
  if ($names != null) {
    foreach ( $names as $name ) {
   		 $this->_list [] = $name;
  	}
  }
}

public function add($name) {
	$this->_list [] = $name;
}

public function find($filter) {
$recs = array ();
foreach ( $this->_list as $user ) {
    if ($filter->filter ( $user ))
    	$recs [] = $user;
    }
   		 return $recs;
    }
}

$ul = new UserList ( 
	array (
    "Andy",
    "Jack",
    "Lori",
    "Megan"
	) 
	);
$f1 = $ul->find ( new FindAfterStrategy ( "J" ) );
print_r ( $f1 );

$f2 = $ul->find ( new RandomStrategy () );

print_r ($f2);

策略模式非常适合复杂数据管理系统或数据处理系统,二者在数据筛选、搜索或处理的方式方面需要较高的灵活性

其他demo

//抽象策略类
abstract class BaseUserStratege{
	abstract public function center();//个人中心
}
//具体策略普通用户策略
class CommonUserStratege extends BaseUserStratege{
  public function center()
  {
  echo 'common user center';
  }
}
//具体策略VIP用户策略
class VipUserStratege extends BaseUserStratege{
  public function center()
  {
  	echo 'vip user center';
  }
}
//环境角色,对抽象策略类的引用
class UserStrategeContext{
  private $baseUserStratege = null;
  public function __construct($baseUserStratege){
  	$this->baseUser = $baseUser;
  }
  public function center(){
  	return $this->baseUserStratege->center();
	}
}

//根据不同用户类型创建不同的具体决策类,比如vip用户,就创建VipUser策略
$user = new VipUserStratege();
$userStrategeContext = new UserStrategeContext($user);
$userStrategeContext->center();

注册模式

注册模式,解决全局共享和交换对象。

已经创建好的对象,挂在到某个全局对象树(可以是数组),在需要使用的时候,直接从该对象树上获取即可。

class Register{
  protected static $objects;//注意是static变量
  //将对象注册到全局的树上
  public function set($alias,$object){
  	self::$objects[$alias] = $object;
  }
  //获取某个注册到树上的对象
  public function get($alias){
  	return self::$objects[$alias];
  }
  //移除某个注册到树上的对象。
  public function delete($alias){
    unset(self::$objects[$alias]);
  }
}

适配器模式

将各种截然不同的函数接口封装成统一的API

PHP中的数据库操作有MySQL,MySQLi,PDO三种,可以用适配器模式统一成一致,使不同的数据库操作,统一成一样的API。

## 接口 IDatabase

<?php
interface IDatabase
{
    function connect($host, $user, $passwd, $dbname);
    function query($sql);
    function close();
}

## MySQL

<?php
class MySQL implements IDatabase
{
    protected $conn;
    function connect($host, $user, $passwd, $dbname)
    {
      $conn = mysql_connect($host, $user, $passwd);
      mysql_select_db($dbname, $conn);
      $this->conn = $conn;
		}
  function query($sql)
  {
      $res = mysql_query($sql, $this->conn);
      return $res;
  }
  function close()
  {
      mysql_close($this->conn);
  }
}

## MySQLi

<?php

class MySQLi implements IDatabase
{
  protected $conn;
  function connect($host, $user, $passwd, $dbname)
  {
      $conn = mysqli_connect($host, $user, $passwd, $dbname);
      $this->conn = $conn;
  }
  function query($sql)
  {
      return mysqli_query($this->conn, $sql);
  }
  function close()
  {
      mysqli_close($this->conn);
  }
}

相关推荐

  1. php常用设计模式应用场景示例

    2024-01-10 20:02:01       18 阅读
  2. 设计模式】23种设计模式概览应用示例

    2024-01-10 20:02:01       7 阅读
  3. 设计模式常见设计模式

    2024-01-10 20:02:01       20 阅读
  4. C#中常见的软件设计模式应用场景

    2024-01-10 20:02:01       27 阅读
  5. 设计模式示例

    2024-01-10 20:02:01       16 阅读
  6. php设计模式

    2024-01-10 20:02:01       18 阅读
  7. 设计模式:策略模式示例

    2024-01-10 20:02:01       10 阅读
  8. 设计模式:组合模式示例

    2024-01-10 20:02:01       12 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-10 20:02:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-10 20:02:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-10 20:02:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-10 20:02:01       20 阅读

热门阅读

  1. 解决Vue.js not detected的问题

    2024-01-10 20:02:01       33 阅读
  2. flink自动发现kafka新增分区

    2024-01-10 20:02:01       38 阅读
  3. OceanBase CentOS7集群部署

    2024-01-10 20:02:01       42 阅读
  4. 面试 React 框架八股文十问十答第四期

    2024-01-10 20:02:01       36 阅读
  5. MySQL面试题(下)

    2024-01-10 20:02:01       32 阅读
  6. 面试 React 框架八股文十问十答第一期

    2024-01-10 20:02:01       39 阅读
  7. glusterFS

    glusterFS

    2024-01-10 20:02:01      32 阅读
  8. linux系统关于nginx服务

    2024-01-10 20:02:01       41 阅读
  9. 修改 Git 仓库的远程地址

    2024-01-10 20:02:01       37 阅读
  10. C#语言练手小功能

    2024-01-10 20:02:01       37 阅读