Three commonly used design patterns in PHP

高洛峰
Release: 2023-03-05 20:12:01
Original
1533 people have browsed it

This article introduces three commonly used PHP design patterns: singleton mode, factory mode, and observer mode. Friends in need can refer to it.

1. First of all, let’s look at the singleton mode

The so-called singleton mode is to ensure that there is only one instance of a certain class, and it instantiates itself and provides it to the entire system. This instance means that only one instance of this class will exist in the application.
Usually the singleton mode is used in instances that only allow database access to objects, thereby preventing multiple database connections from being opened. The singleton mode is a common design pattern. In computer systems, thread pools, caches, log objects, Dialog boxes, printers, database operations, and graphics card drivers are often designed as singletons.

A singleton class should include the following points:
Unlike ordinary classes, singleton classes cannot be instantiated directly, but can only be instantiated by themselves. Therefore, to obtain such restrictive effects, the constructor must be marked private.
In order for a singleton class to function without being instantiated directly, such an instance must be provided for it. Therefore, it is necessary for the singleton class to have a private static member variable that can save the instance of the class and a corresponding public static method that can access the instance.
In PHP, in order to prevent the cloning of the singleton class object from breaking the above implementation form of the singleton class, an empty private __clone() method is usually provided for the base. Well, without further ado, the summary is as follows

The singleton mode has the following 3 characteristics:

1. There can only be one instance, it must have a constructor, and it must be marked private

2. You must create this instance yourself and have a static member variable that saves the instance of the class

3. This instance must be provided to other objects, and there must be a public static method to access this instance

The singleton class cannot be instantiated directly in other classes, but can only be instantiated by itself. It does not create a copy of the instance, but returns a reference to the instance stored inside the singleton class

So why use the PHP singleton pattern?

One of the main application scenarios of PHP is the scenario where the application deals with the database. In an application, there will be a large number of database operations. For the behavior of connecting the database handle to the database, using the singleton mode can avoid a large number of new operations. Because every new operation consumes system and memory resources.

In previous project development, the situation before using the singleton mode was as follows:

//初始化一个数据库句柄$db = new DB(...);//比如有个应用场景是添加一条评论信息$db->addComment();......//如果我们要在另一地方使用这个评论信息,这时要用到数据库句柄资源,可能会这么做......function comment() {
   $db = new DB(...);
   $db->getCommentInfo();......//可能有些朋友也许会说,可以直接使用global关键字!
   global $db;......
Copy after login

It is true that global can solve the problem, and it also plays a role. to the role of the singleton pattern, but in OOP, we recommend rejecting this encoding. Because global has security risks (the unprotected nature of global variables).

Global variables are one of the main causes of BUGs encountered by object-oriented programmers. This is because global variables tie the class to a specific environment, breaking encapsulation. A class that relies on global variables cannot be extracted from one application and applied to a new application if the new application cannot guarantee that the same global variables are defined from the beginning.

To be precise, the singleton mode is an improvement on global variables, which prevents global variables that store unique instances from polluting the namespace. You cannot overwrite a singleton with data of the wrong type. This protection is especially important in versions of PHP that do not support namespaces. Because in PHP naming conflicts are caught at compile time and cause the script to stop running.

We use the singleton mode to improve the following example:

class Single {    private $name;//声明一个私有的实例变量
    private function __construct(){//声明私有构造方法为了防止外部代码使用new来创建对象。    
    }    static public $instance;//声明一个静态变量(保存在类中唯一的一个实例)
    static public function getinstance(){//声明一个getinstance()静态方法,用于检测是否有实例对象
        if(!self::$instance) self::$instance = new self();        return self::$instance;
    }    public function setname($n){ $this->name = $n; }    public function getname(){ return $this->name; }
}$oa = Single::getinstance();$ob = Single::getinstance();$oa->setname('hello php world');$ob->setname('good morning php');echo $oa->getname();//good morning phpecho $ob->getname();//good morning php
Copy after login

The advantages and disadvantages of the singleton mode:

Advantages:

1. Improve system design

2. It is an improvement on global variables

Disadvantages:

1. Difficult to debug

2. Hidden dependencies

3. Unable to overwrite a singleton with wrong type of data

2. Factory pattern

Factory pattern It is a class that contains a method specifically used to create other objects. The factory class is crucial in the practice of polymorphic programming. It allows dynamic replacement of classes and modification of configurations, which usually makes the application more flexible. Flexibility and proficiency in factory patterns are important for senior PHP developers.

The factory pattern is usually used to return different classes that conform to similar interfaces. A common use of factories is to create polymorphic providers, allowing us to decide which ones should be instantiated based on application logic or configuration settings. A class, for example, can be extended using such a provider to use the new extended name without the need to refactor other parts of the application.

Usually, the factory pattern has a key construct, which is a static method named Factory according to the general principle. However, this is just a principle. The factory method can be named arbitrarily. This static method can also accept parameters of any data. It must be Return an object.

具有为您创建对象的某些方法,这样就可以使用工厂类创建对象,工厂模式在于可以根据输入参数或者应用程序配置的不同来创建一种专门用来实现化并返回其它类的实例的类,而不直接使用new,这样如果想更改创建的对象类型,只需更改该工厂即可,

先举个示例吧:

<?phpclass  Factory {//创建一个基本的工厂类
    static public function fac($id){//创建一个返回对象实例的静态方法
        if(1 == $id) return new A();        
        elseif(2==$id) return new B();        
        elseif(3==$id) return new C();        
        return new D();
    }
}interface FetchName {//创建一个接口
    public function getname();//}class A implements FetchName{    private $name = "AAAAA";    public function getname(){ return $this->name; }
}class C implements FetchName{    private $name = "CCCCC";    public function getname(){ return $this->name; }
}class B implements FetchName{    private $name = "BBBBB";    public function getname(){ return $this->name; }
}class D implements FetchName{    private $name = "DDDDD";    public function getname(){ return $this->name; }
}$o = Factory::fac(6);//调用工厂类中的方法if($o instanceof FetchName){  echo  $o->getname();//DDDDD}$p=Factory::fac(3);echo $p->getname();//CCCCC?>
Copy after login

个人意见,再说简单点吧,PHP工厂模式就是用一个工厂方法来替换掉直接new对象的操作,就是为方便扩展,方便使用,在新增实现基类中的类中方法时候,那么在工厂类中无需修改,传入参数可以直接使用,具体就是跳过工厂类修改,直接使用工厂类输出想要的结果。在传统习惯中,如果要生成一个类的话,在代码中直接new一个对象,比如:

class Database{
    
} 
$db = new Database();
Copy after login

下面介绍工厂模式的操作方法:

class Database{  
 
} 
//创建一个工厂类class Factory
{   //创建一个静态方法
   static function createDatabase(){       
   $db = new Database;       
   return $db;
   }
}
Copy after login

那么,当我们想创建一个数据库类的话,就可以使用这样的方法:

<?php  
    $db = Factory::createDatabase();?>
Copy after login

简单工厂模式比直接new一个对象的好处是,比如Database这个类在很多php文件中都有使用到,当Database这个类发生了某些变更,比如修改了类名、或者一些参数发生了变化,那这时候如果你使用的是$db = new Database这种传统方法生成对象,那么在所有包含这种生成对象的php文件代码中都要进行修改。而使用工厂模式,只要在工厂方法或类里面进行修改即可。而且工厂模式是其他设计模式的基础。
对上面的简单工厂模式再进一步优化,比如:

 利用工厂类生产对象:

<?phpclass  Example
{    // The parameterized factory method
    public static function factory($type)
    {        if (include_once &#39;Drivers/&#39; . $type . &#39;.php&#39;) 
    {            $classname = &#39;Driver_&#39; . $type;
                return new $classname;
        } 
        else 
        {            
        throw new Exception(&#39;Driver not found&#39;);
        }
    }
} 
// Load a MySQL Driver$mysql = Example::factory(&#39;MySQL&#39;); 
// Load an SQLite Driver$sqlite = Example::factory(&#39;SQLite&#39;);?>
Copy after login

简单工厂模式又称静态工厂方法模式。从命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口。
要理解工厂模式这个概念,让我们最好谈一下许多开发人员从事大型系统的艰苦历程。在更改一个代码片段时,就会发生问题,系统其他部分 —— 您曾认为完全不相关的部分中也有可能出现级联破坏。
该问题在于紧密耦合 。系统某个部分中的函数和类严重依赖于系统的其他部分中函数和类的行为和结构。您需要一组模式,使这些类能够相互通信,但不希望将它们紧密绑定在一起,以避免出现联锁。
在大型系统中,许多代码依赖于少数几个关键类。需要更改这些类时,可能会出现困难。例如,假设您有一个从文件读取的 User 类。您希望将其更改为从数据库读取的其他类,但是,所有的代码都引用从文件读取的原始类。这时候,使用工厂模式会很方便。

看下实例:

<?php  
    interface IUser
    {      function getName();
    } 
    class User implements IUser
    {        
      public $id;      public function __construct( $id ) { } 
      public function getName()
      {        return "Fantasy";
      }
    }?>
Copy after login

传统方法使用 User 类,一般都是这样:

<?php //在页面1$obj = new User(1); 
//在页面2$obj2 = new User(2); 
//在页面3$obj3 = new User(3);....

?>
Copy after login

这时候,由于新的需求,使得User类要新增个参数或者User类名称发生变化,User 类代码发生变动,即:

<?phpclass  User implements IUser
{  public $id,$pre;  public function __construct( $id , $pre = &#39;&#39;) {...} 
  public function getName()
  {    return $this->pre."Fantasy";
  }
}?>
Copy after login

接着,恐怖的事情发生了,假设之前有 100 个页面引用了之前的 User 类,那么这 100 个页面都要发生相应的改动:

//在页面1$obj = new User(1,'aaa'); 
//在页面2$obj = new User(2,'aaa'); 
//在页面3$obj = new User(3,'aaa');...
Copy after login

本来是一个小小的改动,但因紧密耦合的原因使得改动大吐血。而使用工厂模式则可以避免发生这种情况:

//User类为变动前class UserFactory
{  public static function Create( $id )
  {    return new User( $id );
  }
} 
//页面1$uo1 = UserFactory::Create( 1 ); 
//页面2$uo12 = UserFactory::Create( 2 );....
Copy after login

这时候需求变动,User 类也发生变动:

<?phpclass  User implements IUser
{  public $id,$pre;  public function __construct( $id , $pre = &#39;&#39;) {...} 
  public function getName()
  {    return $this->pre."Jack";
  }
}?>
Copy after login

但是,我们不再需要去改动这 100 个页面,我们要改的仅仅是这个工厂类:

//class UserFactory
{  public static function Create( $id,$pre = 'aaa' )
  {    return new User( $id ,$pre);
  }
}
Copy after login

其他100个页面不用做任何改动,这就是工厂设计模式带来的好处。看下UML图:

Three commonly used design patterns in PHP

三、观察者模式
观察者模式为您提供了避免组件之间紧密耦合的另一种方法。该模式非常简单:观察者模式是一种事件系统,意味着这一模式允许某个类观察另一个类的状态,当被观察的类状态发生改变的时候,观察类可以收到通知并且做出相应的动作;

现在有两派,有的人建议使用设计模式,有的人不建议使用设计模式!
这就好比写文章一样,有的人喜欢文章按照套路走,比如叙事性质的文章,时间,地点,人物,事件。而有的人喜欢写杂文或者散文,有的人喜欢写诗词!
现在写代码很多地方类似于写文章,但是在有些地方比写文章需要更多的技能!写文章写多了一般也能写出优秀的文章,而代码也一样,写多了也能写出很多有写的代码!
很多时候,我看设计模式的时候,有些设计模式只是吻合我的代码习惯。但是你硬去套它,那么反而适得其反。——很多时候是学会了招式,在应用中不知不觉的使用上这些招式,才能掌握其道,但是也不要拘泥于招式,正所谓“无招胜有招”吗?

我学设计模式的初衷,就是知道有这么个玩意儿?脑子里有这么个印象,也不会生套它!如果设计模式不符合你的习惯对你阅读代码反而是不利的!

观察者模式定义对象的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新!


设计原则 

在观察者模式中,会改变的是主题的状态以及观察者的数目。用这个模式,你可以改变依赖于主题状态的对象,却不必改变主题。——找出程序中会变化的方面,然后将其和固定不变的方面相分离!

 

主题和观察者都使用接口:观察者利用主题的接口向主题注册,而主题利用观察者接口通知观察者。这样可以让两者之间运作正常,又同时具有松耦合的优点! ——针对接口编程,不针对实现编程!

 

观察者模式利用“组合”将许多观察者组合进主题中。对象(观察者——主题)之间的这种关系不是通过继承产生的,而是在运行时利用组合的方式产生的。 ——多用组合,少用继承!

好了,不说太多废话,直接上代码:

<?php /**
 * 观察者模式
 * @author: Fantasy
 * @date: 2017/02/17 */
 class Paper{ /* 主题    */
    private $_observers = array(); 
    public function register($sub){ /*  注册观察者 */
        $this->_observers[] = $sub;
    }     
    public function trigger(){  /*  外部统一访问    */
        if(!empty($this->_observers)){
                    foreach($this->_observers as $observer)
                    {
                                    $observer->update();
            }
        }
    }
} 
/**
 * 观察者要实现的接口 */interface Observerable
 {    public function update();
} 
class Subscriber implements Observerable{
    public function update()
    {        echo "Callback\n";
    }
}?>
Copy after login

下面是测试代码:

/*  测试    */
$paper = new Paper();
$paper->register(new Subscriber());
//$paper->register(new Subscriber1());
//$paper->register(new Subscriber2());$paper->trigger();
Copy after login

总结

       当新对象要填入的时候,只需要在主题(又叫可观察者)中进行注册(注册方式很多,你也可以在构造的时候,或者框架访问的接口中进行注册),然后实现代码直接在新对象的接口中进行。这降低了主题对象和观察者对象的耦合度。

好的设计模式不会直接进入你的代码中,而是进入你的大脑中。

更多PHP常用的三种设计模式 相关文章请关注PHP中文网!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!