zoukankan      html  css  js  c++  java
  • 「七天自制PHP框架」第三天:PHP实现的设计模式

    往期回顾:「七天自制PHP框架」第二天:模型与数据库,点击此处

    原文地址:http://www.cnblogs.com/sweng/p/6624845.html,欢迎关注:编程老头

    为什么要使用设计模式?

    设计模式,我的理解是为了达到“可复用”这个目标,而设计的一套相互协作的类。

    感兴趣的读者可以阅读《Design Patterns: Elements of Reusable Object-Oriented Software》,四位作者(Gang of Four)在书中列举了业界闻名的23种设计模式。

    这里先介绍我们框架要涉及的三种设计模式。

    单例模式(singleton)

    单例模式可以保证一个类只有一个对象实例, 常用在数据库存取类,从而节省硬件资源的消耗。

    这里,我们改写上一章节的MySQL类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    class MySQL extends DB{
        private static $instance=null;
        public static function getInstance(){
            if(self::$instance==null){
                self::$instance=new MySQL();
            }
            return self::$instance;
        }
        public function MySQL(){
             
            /*Config*/
            $this->IP='*';
            $this->ServerID='*';
            $this->ServerPassword='*';
            $this->DataBaseName='*';
            /*End of Config*/
             
            $this->connection=mysqli_connect($this->IP,$this->ServerID,$this->ServerPassword,$this->DataBaseName);
             
            if(!$this->connection){
                die('Could not connect'.$this->connection);
            }
             
            mysqli_query($this->connection,'set names utf8');
        }
        public function Execute($sql){
            return mysqli_query($this->connection,$sql);
        }
        public function Query($sql){
            $result=mysqli_query($this->connection,$sql);
            $arr=array();
            while($row=mysqli_fetch_array($result)){
                $arr[]=$row;
            }
            return $arr;
        }
        public function Close(){
            mysqli_close($this->connection);
        }
    }

    这里要注意的是,如果实例化一个MySQL类,我们不再写

    1 $db=new MySQL();

    而是这样:

    1 $db=MySQL::getInstance();

    因为只有getInstance这个静态函数,才能保证只调用一次MySQL类的构造函数。

    单例模式是很常用的设计模式,这里不再赘述。

    外观模式(Facade)

    因为命名空间的问题,外观模式可以保证一个类的诸多方法看似是“一个类提供的”,这里我们先设计一个简单的服务提供者类

    1
    2
    3
    4
    5
    class ServiceProvider{
        public function Write($arg){
            echo $arg;
        }
    }

    这个类只有一个Write方法,就是把参数打印出来

    然后定义一个Facade类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Facade{
        public static function getInstance($classname,$args){
            return new $classname($args);
        }
         
        public static function getFacadeAccessor(){
            //
        }
         
        public static function __callstatic($method,$args){
            $instance=static::getInstance(static::getFacadeAccessor(),$args);
            return call_user_func_array(array($instance,$method),$args);
        }
    }

    要理解这个类,我们只要关注最后一个函数,就是__callstatic魔术方法。这个方法就是Facade类型对象或者其子类在调用他自身没有定义过的函数时,就会调用__callstatic方法,而这个方法最后调用了call_user_func_array函数,就是把任务交给提供这项服务的类去完成,同时完成参数的传递。

    我们再写一个Facade子类

    1
    2
    3
    4
    5
    class MyFacade extends Facade{
        public static function getFacadeAccessor(){
            return ServiceProvider::class;
        }
    }

    这里注意,子类实现了父类没有具体实现的getFacadeAccessor方法,这个方法就是要告诉父类的__callstatic方法:“我作为Facade,代表的是什么哪个类,任务就由他来实现吧”,从语法上看,只是返回了一个表示类名的字符串。所以父类起初并不知道它的子类都代表着什么“服务提供者类”,只有当子类的静态函数被调用后,因为子类没有该静态函数,所以父类的__callstatic方法被启动了。

    抽象工厂(Factory)

    我对抽象工厂有一个粗俗的理解:“对象与字符串的对应”,也就是用一个字符串就可以创造一个类的对象。这种做法主要用在两种情况下是很方便的:

    1.类名不稳定,会在项目中频繁修改

    类名修改,很多时候并不是设计者的“命名洁癖”或者“命名强迫症”导致的修改,而是在项目的不断迭代,发觉这个类设计的不合理。如果这个类用的不频繁,那么改个类名只要手工做一些小的修改即可,但是如果这个类通篇存在于代码之中(假如是数据库类),那修改工作量就大了,当然,我们也可以对代码文件使用“字符串替换”,但是假如一个PHP写成的项目,PHP文件有几十上百个,这也是不合理的事。

    2.类的设计者并不是类的使用者

    类的设计者和类的使用者不是同一个开发人员,那么记忆一个字符串或许比记忆一个类名要生动的多。我们都学过计算机网络原理,都知道记忆一个域名要比记忆一个IP地址要生动的多,这就是DNS解决的问题。

    因为抽象工厂很多教材都有涉及,不再赘述,本文将介绍一下目前非常流行的服务容器。

    我们希望整个工程项目中,DB类,Session类,FileSystem类“拿来即用”,不用每次繁琐的初始化,比如写$db=new DB(arg1,arg2);这类语句,也希望DB等类型的对象像一个“全局”变量一般,在整个程序运行期间,随时可以调用。

    服务容器可以让调用DB等类型的程序员不用知道这个类太多的细节,甚至可以用一个字符串的别名来创建这样一个对象。

    我们定义一个服务容器类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Container{
        public $bindings;
        public function bind($abstract,$concrete){
            $this->bindings[$abstract]=$concrete;
        }
        public function make($abstract,$parameters=[]){
            return call_user_func_array($this->bindings[$abstract],$parameters);
        }
    }

    可以把服务容器简单的看成一个全局变量,bind方法就是用关联数组把字符串和构造函数做绑定。

    至此,有了服务容器,我们的Model类就要做修改了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class Model implements IModel{
        public static $table;
        public static $container;
         
        public static $db;
        public function __construct(){
            self::$container=new Container();
            self::$container->bind('db',function(){
                return MySQL::getInstance();
            });
             
            self::$db=self::$container->make('db',[]);
        }
         
        public static function get($id){
            return self::where('id',$id);
        }
         
        public static function where($condition,$value){
            $sql=sprintf("select * from %s where %s='%s'",self::$table,$condition,$value);
            return self::$db->Query($sql);
        }
        public static function all(){
            $sql=sprintf("select * from %s",self::$table);
            return self::$db->Query($sql);
        }
    }

    观察上面代码,我们同时用了单例模式和服务容器。

    总结:如果要做一个PHP框架,应该要做好代码的复用。设计模式一直是很多争论的焦点,“究竟该不该使用设计模式?”,本文开始,我也努力回避“过于纠结这个问题”,我认为,设计模式有其存在的价值,至少在具体项目中,确实在很多版本迭代中节省了工作量,提高工作效率,但是如果在一个小项目中为了“秀一下我会设计模式”而使用设计模式,就不合理了。

  • 相关阅读:
    软件升级细节卸载删除快捷方式等前需要检测
    安装gitlab的总结
    如何修改vagrant系统的root用户密码
    写一个PHP单例模式
    redis使用笔记
    mysql 删除商品名字重复数据,同时保留最新一条
    杀死僵尸进程
    Django 用户状态管理,认证,失效
    关于iOS多任务的一些扫盲
    ajax异步
  • 原文地址:https://www.cnblogs.com/wuyuxin/p/7039722.html
Copyright © 2011-2022 走看看