>  기사  >  백엔드 개발  >  PHP에서 일반적으로 사용되는 세 가지 디자인 패턴

PHP에서 일반적으로 사용되는 세 가지 디자인 패턴

高洛峰
高洛峰원래의
2017-02-18 16:18:341516검색

이 글에서는 일반적으로 사용되는 세 가지 PHP 디자인 패턴인 싱글톤 모드, 팩토리 모드, 관찰자 ​​모드를 소개합니다.

1. 우선 싱글턴 모드를 살펴보자

일명 싱글턴 모드는 특정 클래스의 인스턴스가 하나만 존재하도록 보장하는 것이며, 그리고 자체적으로 인스턴스화하여 전체 시스템에 제공합니다. 이 인스턴스는 이 클래스의 인스턴스가 하나만 애플리케이션에 존재함을 의미합니다.
일반적으로 싱글톤 모드는 개체에 대한 데이터베이스 액세스만 허용하여 여러 데이터베이스 연결이 열리는 것을 방지하는 인스턴스에 사용됩니다. 싱글톤 모드는 컴퓨터 시스템, 스레드 풀, 캐시, 로그 개체, 대화 상자에서 일반적인 디자인 패턴입니다. 박스, 프린터, 데이터베이스 작업 및 그래픽 카드 드라이버는 종종 싱글톤으로 설계됩니다.

싱글턴 클래스에는 다음 사항이 포함되어야 합니다.
일반 클래스와 달리 싱글턴 클래스는 직접 인스턴스화할 수 없고 자체적으로만 인스턴스화할 수 있습니다. 따라서 이러한 제한적인 효과를 얻으려면 생성자를 비공개로 표시해야 합니다.
싱글톤 클래스가 직접 인스턴스화되지 않고 작동하려면 해당 인스턴스를 제공해야 합니다. 따라서 싱글톤 클래스에는 클래스의 인스턴스를 저장할 수 있는 전용 정적 멤버 변수와 해당 인스턴스에 액세스할 수 있는 공용 정적 메서드가 필요합니다.
PHP에서는 싱글톤 클래스 객체의 복제로 인해 위의 싱글톤 클래스 구현 형식이 깨지는 것을 방지하기 위해 일반적으로 기본으로 빈 프라이빗 __clone() 메서드가 제공됩니다. 자, 더 이상 고민하지 않고 요약하면 다음과 같습니다.

싱글턴 모드는 다음과 같은 3가지 특징을 가지고 있습니다.

1. 인스턴스는 하나만 있을 수 있고 생성자가 있어야 하며 비공개로 표시되어야 합니다

2. 이 인스턴스를 직접 만들어야 하며

클래스의 인스턴스를 저장하는 정적 멤버 변수가 있어야 합니다. 이 인스턴스는 다른 객체에 제공되어야 하며, 이 인스턴스에 접근하기 위한 public static 메소드가 있습니다

싱글턴 클래스는 다른 클래스에서 직접 인스턴스화할 수 없고, 자체적으로만 인스턴스화할 수 있습니다. 인스턴스의 복사본을 생성하지 않고 싱글톤 클래스 내부에 저장된 인스턴스에 대한 참조를 반환합니다

그렇다면 PHP 싱글톤 패턴을 사용하는 이유는 무엇일까요?

PHP의 주요 애플리케이션 시나리오 중 하나는 애플리케이션이 데이터베이스를 다루는 시나리오입니다. 애플리케이션에서는 데이터베이스 핸들을 데이터베이스에 연결하는 동작에 대해 많은 수의 데이터베이스 작업이 발생합니다. , 싱글톤 모드를 사용하면 많은 수의 새로운 작업을 피할 수 있습니다. 모든 새로운 작업은 시스템 및 메모리 리소스를 소비하기 때문입니다.

이전 프로젝트 개발에서 싱글턴 모드를 사용하기 전의 상황은 다음과 같았습니다.

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

Global이 문제를 해결할 수 있다는 것은 사실이지만, 싱글톤 패턴의 역할에도 작동하지만 OOP에서는 이 인코딩을 거부하는 것이 좋습니다. 글로벌에는 보안 위험이 있기 때문입니다(글로벌 변수의 보호되지 않는 특성).

전역 변수는 객체 지향 프로그래머가 직면하는 BUG의 주요 원인 중 하나입니다. 이는 전역 변수가 클래스를 특정 환경에 연결하여 캡슐화를 깨뜨리기 때문입니다. 전역 변수에 의존하는 클래스는 새 응용 프로그램이 처음부터 동일한 전역 변수가 정의되어 있음을 보장할 수 없는 경우 하나의 응용 프로그램에서 추출하여 새 응용 프로그램에 적용할 수 없습니다.

정확하게 말하면 싱글턴 모드는 전역 변수를 개선한 것으로, 고유한 인스턴스를 저장하는 전역 변수가 네임스페이스를 오염시키는 것을 방지합니다. 잘못된 유형의 데이터로 싱글톤을 덮어쓸 수 없습니다. 이러한 보호는 네임스페이스를 지원하지 않는 PHP 버전에서 특히 중요합니다. PHP에서는 이름 지정 충돌이 컴파일 타임에 포착되어 스크립트 실행이 중지되기 때문입니다.

다음 예를 개선하기 위해 싱글턴 패턴을 사용합니다.

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

싱글턴 패턴의 장점과 단점:

장점:

1. 시스템 디자인 개선

2. 전역 변수 개선

단점:

1.

2. 숨겨진 종속성

3. 잘못된 유형의 데이터로 싱글톤을 덮어쓸 수 없습니다

2. 팩토리 패턴(Factory Pattern) 다른 객체를 생성하는 데 특별히 사용되는 메소드를 포함하는 클래스입니다. 팩토리 클래스는 클래스를 동적으로 대체하고 구성을 수정하는 데 매우 중요하며, 이는 일반적으로 애플리케이션을 보다 유연하게 만듭니다. 고급 PHP 개발자에게는 팩토리 패턴에 대한 숙련도가 중요합니다.

팩토리 패턴은 일반적으로 유사한 인터페이스를 준수하는 다양한 클래스를 반환하는 데 사용됩니다. 팩토리의 일반적인 용도는 다형성 공급자를 생성하여 애플리케이션 논리 또는 구성 설정에 따라 인스턴스화해야 하는 클래스를 결정할 수 있도록 하는 것입니다. 예를 들어, 애플리케이션의 다른 부분을 리팩터링할 필요 없이 새 확장 이름을 사용하도록 해당 공급자를 사용하여 클래스를 확장할 수 있습니다.

일반적으로 팩토리 패턴에는 일반 원칙에 따라 Factory라는 정적 메서드인 핵심 구성이 있지만 이는 원칙일 뿐이며 이 정적 메서드는 임의로 이름을 지정할 수도 있습니다. 모든 데이터의 매개변수를 허용합니다. 객체를 반환해야 합니다.

具有为您创建对象的某些方法,这样就可以使用工厂类创建对象,工厂模式在于可以根据输入参数或者应用程序配置的不同来创建一种专门用来实现化并返回其它类的实例的类,而不直接使用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?>

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

class Database{
    
} 
$db = new Database();

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

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

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

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

简单工厂模式比直接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;);?>

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

看下实例:

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

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

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

?>

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

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

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

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

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

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

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

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

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

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

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

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";
    }
}?>

下面是测试代码:

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

总结

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

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

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

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.