PHP设计模式

16 篇文章 0 订阅

PHP设计模式——概述

 

 

常见的面向对象设计模式大约有23种,但是自从接触PHP后,渐渐发现常见的设计模式好像少了很多,网络上的资料也比较少,身边的PHP同事们有的甚至没有听说过设计模式,这也有可能是PHP的发展所带来的,因为PHP对面向对象支持的比较晚,好多PHP程序员还按照面向过程的思想写代码。于是,我决定把原来用C#写的面向对象设计模式用PHP改写。

      

        经常听见其他程序员(Java、C#)等说PHP是不是运行在浏览器端的脚本语言,其实我在接触PHP之前也这样认为过,后来发现PHP是用C语言开发出来的一种语言,C语言是真正意义上跨平台的语言,这也注定PHP是跨平台的,PHP是可运行在Windows Server或Linux操作系统的服务器上的语言,它和Java以及C#一样,代码存储并运行在服务器端,它将浏览器端可执行的HTML以及脚本发送给浏览器执行,PHP相对Java和C#对于面向过程的封装更多,减少部分数据类型的支持。


        PHP是面向对象的,PHP能够得到这样快速的发展,能够被互联网大企业所广泛应用,证明PHP是经得住考验的,随着移动互联网的不断发展,PHP还会更广阔的天地。

  

      什么是设计模式?

       设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。     


        项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。


       设计模式的优点:

        1).复用解决方案: 设计模式本身就是对某一类问题的通用解决方案,是更高级别的复用,已经超出了代码复用.

        2).确定通用术语:开发中的交流和协作都需要共同的词汇其础和对问题的共识. 当你有表达却又表达不清楚的设计思路,即使表达出来也会被同事误解的时候,设计模式就显出沟通的优势了。

        3).代码更易于修改与维护。因为设计模式都是久经考验的解决方案,它们的结构都是经过长期的发展形成的,善于应对变化,设计模式本身也是对变化点的封装。

        4).模式有助于提高思考层次。学习模式后,就算不用模式中的方法,也会更好的采取更好的策略去解决问题。



        在接下来的篇幅里会详细介绍PHP面向对象设计模式,本系列博客以设计模式的原理为核心,列举最简单的例子,让只要有一点面向对象基础的读者都能看明白。

 

 

PHP设计模式——六大原则


 

     一般认为遵从以下六大原则的代码是易扩展可复用的代码:

 

                           

 

     

 

     这六大原则任何面向对象的语言都应该遵守的,要想让你的代码易扩展高服用就尽量去满足这六大原则吧,不一定严格按照某种设计模式,但是如果你的代码符合这六大原则,那么你的代码就是好代码了,好的代码不一定是严格按照设计模式写的代码。

 


 

         1.单一职责

 

        定义:不要存在多于一个导致类变更的原因。通俗的说,即一个只负责一项职责。

 

         场景:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障,关系如下图:

 

                                                                                            

 


 

        修改:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险,结构如下图:

 

                                           

 

      优点:

 

            1)、可以降低类的复杂度,一个类只负责一项职责,逻辑简单;

 

             2)、提高类的可读性,提高系统的可维护性;

 

             3)、变更引起的风险降低,变更是必然的。

    

    2.里氏代换原则

 

     定义:所有引用基类的地方必须能透明地使用其子类的对象,也就是说类可以扩展父类的功能,但不能改变父类原有的功能

 

     场景:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障,如下图:

 


 

                                       

 

        CountPriceByJKL类继承于CountPrice类,CountPriceByJKL重写了Count()方法,这样可能影响到原来Count方法的功能。

 


 

        修改:当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。

 


 

        3.依赖倒置原则

 

        定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。

 

        此处理解起来是最困难的,一般会在项目框架的搭建的时候用到,例如,业务逻辑层相对于数据层是高层模块,因为业务逻辑层需要调用数据层去连接数据库,但是要做到可扩展高复用,尽量不要让业务逻辑层依赖数据层,可以在数据层抽象出一个接口,让业务逻辑层依赖于这个抽象接口。

 

         场景:A(高层模块)直接依赖类B(低层模块),假如要将类A改为依赖类C(低层模块),则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险

 

                                                             

 

          AutoSystem类直接依赖于HondaCar与FordCar两个类,这样就产生了一个高耦合,AutoSystem类想操控HondaCar或者FordCar必须直接创建相应对象。

 

         修改:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率,如下图:

 

                          

 

           经过此番修改,Honda与Ford实现ICar接口,提供了Run、Stop以及Turn功能方法,AutoSystem依赖ICar接口,这样迫使AutoSystem依赖抽象接口,这就使得AutoSystem类能够应对更多的需求变化。

 


 

          优点:

 

                1)、低层模块尽量都要有抽象类或接口,或者两者都有。

 

                 2)、变量的声明类型尽量是抽象类或接口。

 

                 3)、使用继承时遵循里氏替换原则。



 

           4.接口隔离原则

 

           定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。

 


 

           场景:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法,如下图:

 


 


 

        

 

   修改:将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。

 


 

        

 

   注意:

 

 

 

     1)、接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性   是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。

 

        2)、为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。

      3)、提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。


 

    5.迪米特法则(最少知道原则)

 

  定义:一个对象应该对其他对象保持最少的了解。

 

  场景:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大

 

  简单的理解就是高内聚,一个类尽量减少对其他对象的依赖,并且这个类的方法和属性能用私有的就尽量私有化。

 


 

  注意:

 

            1)、只与直接的朋友通信,不要和陌生人说话。

 

            2)、过分的使用该原则,将导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。

 


 

        6.开闭原则

 

        定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭

 

        场景:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试

 

       建议:当软件需求变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

 

 

 

PHP设计模式——简单工厂

前面两节内容介绍了什么是设计模式以及六大原则,相信看完前两节内容大家对设计模式已经有了初步的认识,接下来说一下设计模式的分类。


        一般将面向对象设计模式分为三类:创建型、结构型、行为型三种。


        创建型:创建对象时,不再由我们直接实例化对象;而是根据特定场景,由程序来确定创建对象的方式,从而保证更大的性能、更好的架构优势。创建型模式主要有简单工厂模式(并不是23种设计模式之一)、工厂方法、抽象工厂模式、单例模式、生成器模式、原型模式。


       结构型:用于帮助将多个对象组织成更大的结构。结构型模式主要有适配器模式、桥接模式、组合器模式、装饰器模式、门面模式、亨元模式和代理模式。


       行为型:用于帮助系统间各对象的通信,以及如何控制复杂系统中流程。行为型模式主要有命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板模式、访问者模式和职责链模式。

          

       今天主要介绍创建型的第一种简单工厂模式。

       注意:在阅读本系列博客的时候一定要有阅读UML类图、面向对象PHP编程基础。


      

       简单工厂模式不属于23种常用面向对象设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。其实质是由一个工厂类根据传入的参数动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。


                                    

       角色及职责:  

       工厂(SimpleFactory)角色:简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。

       抽象产品(IProduct)角色:简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。

       具体产品(Concrete Product)角色:是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。

      需求:根据提供相应的属性值由简单工厂创建具有相应特性的产品对象。

      现根据以上UML类图编写如下PHP代码。

     

    <?php  
    /** 
     * Created by PhpStorm. 
     * User: Jiang 
     * Date: 2015/4/9 
     * Time: 21:48 
     */  
      
    /**抽象产品角色 
     * Interface IProduct   产品接口 
     */  
    interface IProduct  
    {  
        /**X轴旋转 
         * @return mixed 
         */  
        function XRotate();  
      
        /**Y轴旋转 
         * @return mixed 
         */  
        function YRotate();  
    }  
      
    /**具体产品角色 
     * Class XProduct        X轴旋转产品 
     */  
    class XProduct implements IProduct  
    {  
        private $xMax=1;  
        private $yMax=1;  
      
        function __construct($xMax,$yMax)  
        {  
            $this->xMax=$xMax;  
            $this->yMax=1;  
        }  
      
        function XRotate()  
        {  
            echo "您好,我是X轴旋转产品,X轴转转转。。。。。。";  
        }  
      
        function YRotate()  
        {  
            echo "抱歉,我是X轴旋转产品,我没有Y轴。。。。。。";  
        }  
    }  
      
    /**具体产品角色 
     * Class YProduct        Y轴旋转产品 
     */  
    class YProduct implements IProduct  
    {  
        private $xMax=1;  
        private $yMax=1;  
      
        function __construct($xMax,$yMax)  
        {  
            $this->xMax=1;  
            $this->yMax=$yMax;  
        }  
      
        function XRotate()  
        {  
            echo "抱歉,我是Y轴旋转产品,我没有X轴。。。。。。";  
        }  
      
        function YRotate()  
        {  
            echo "您好,我是Y轴旋转产品,Y轴转转转。。。。。。";  
        }  
    }  
      
    /**具体产品角色 
     * Class XYProduct        XY轴都可旋转产品 
     */  
    class XYProduct implements IProduct  
    {  
        private $xMax=1;  
        private $yMax=1;  
      
        function __construct($xMax,$yMax)  
        {  
            $this->xMax=$xMax;  
            $this->yMax=$yMax;  
        }  
      
        function XRotate()  
        {  
            echo "您好,我是XY轴都可旋转产品,X轴转转转。。。。。。";  
        }  
      
        function YRotate()  
        {  
            echo "您好,我是XY轴都可旋转产品,Y轴转转转。。。。。。";  
        }  
    }  
      
    /**工厂角色 
     * Class ProductFactory 
     */  
    class ProductFactory  
    {  
        static function GetInstance($xMax,$yMax)  
        {  
            if($xMax>1 && $yMax===1)  
            {  
                return new XProduct($xMax,$yMax);  
            }  
            elseif($xMax===1 && $yMax>1)  
            {  
                return new YProduct($xMax,$yMax);  
            }  
            elseif($xMax>1 && $yMax>1)  
            {  
                return new XYProduct($xMax,$yMax);  
            }  
            else  
            {  
                return null;  
            }  
        }  
    }  



         测试代码:

    <?php  
    /** 
     * Created by PhpStorm. 
     * User: Jiang 
     * Date: 2015/4/9 
     * Time: 21:54 
     */  
    require_once "./SimpleFactory/SimpleFactory.php";  
      
    header("Content-Type:text/html;charset=utf-8");  
      
    $pro=array();  
    $pro[]=ProductFactory::GetInstance(1,12);  
    $pro[]=ProductFactory::GetInstance(12,1);  
    $pro[]=ProductFactory::GetInstance(12,12);  
    $pro[]=ProductFactory::GetInstance(0,12);  
      
    foreach($pro as $v)  
    {  
        if($v)  
        {  
            echo "<br/>";  
            $v->XRotate();  
            echo "<br/>";  
            $v->YRotate();  
        }  
        else  
        {  
            echo "非法产品!<br/>";  
        }  
        echo "<hr/>";  
    }  



      用浏览器访问测试代码,我们可以发现创建的对象依次是YProduct,XProduct,XYProduct,null。简单工厂的核心代码在于工厂(ProductFactory)这个角色,这里根据传入的xMax与yMax值去创建不同的对象,这便是简单工厂的实质,而且我们在测试调用客户端根本不知道具体的产品类是什么样,这样就做到了调用与创建的分离。


       简单工厂的优点:让对象的调用者和对象创建过程分离,当对象调用者需要对象时,直接向工厂请求即可。从而避免了对象的调用者与对象的实现类以硬编码方式耦合,以提高系统的可维护性、可扩展性。

       简单工厂的缺点:当产品修改时,工厂类也要做相应的修改,比如要增加一种操作类,如求M数的N次方,就得改case,修改原有类,违背了开放-封闭原则


转自:http://www.cnblogs.com/aipiaoborensheng/p/5730570.html


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值