源码解析容器以及面门模式Facade深度解刨,反射机制

单利模式

解决场景就是如何在整个项目创建一个唯一对象实例

拥有一个构造函数,并且为privare
拥有一个静态成员变量用来保持类的实例
拥有一个访问这个实例的静态方法

注册树模式

注册树模式通过将对象实例注册到一棵全局的对象树上
需要的时候从对象树上采摘下来使用

把一些常用的基础类库或者对象统一挂载这个树上,不用new直接用树上取出来

后续把对象挂载到$objects上

#A.php A类

class A { 
	public function abc() { 
		return "abcd";
	}
}

A类挂载到数下面

class SingwaRegister {
	/*
		注册树池子	
	*/
	protected static $object = null;
	
	/*
		将对象挂到树上 
	*/
	#第一个参数是key,第二个参数是对象
	public static function set($key,$object){ 
		self::$object[$key] = $object;
	}

	/*
		从树上获取对象,如果没有的时候 注册
	*/
    public static function get($key) {
    	if(!isset(self::$object[$key])){
    	#挂载
    		self::$object[$key] = new $key;
    	}
    	return self::$object[$key];
    }

   public static function __unset($key) { 
   		unset(self::$object[$key]);
   }  
}
public function register() { 

	$a = new \A();

	\SingwaRegister::set("singwa",$a);

	$abc = \SingwaRegister::get("singwa")->abc();

	var_dump($abc);
}

在这里插入图片描述

如何理解依赖注入和控制反转

依赖注入和控制反转是同一个东西,就是编程思想不同

依赖注入主要用来减少代码间的耦合
有效分离对象和它所需的外部资源

注入谁注入谁呢

class Person { 
	/*
		依赖   Person类依赖于 Car
		注入  car类注入到Person
	*/
	#传入对象 
	public function buy($obj) { 
		#$bmw = new Car();
		#return $bmw->pay();
		return $obj->pay(); 
	}
}

#M.php 或者Car.php

namespace di;
class M { 
	public function pay() { 
		return 345;
	}
}
public function personbuy() { 
	$signwa = new \di\Person();
	$bmw = new \di\M;
	echo $singwa->buy($bmw);
}

在这里插入图片描述
在这里插入图片描述

PHP反射机制深入学习

一、什么是反射?
它是指在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。这种动态获取的信息以及动态调用对象的方法的功能称为反射API。
在PHP中,反射是指在PHP运行状态中,扩展分析PHP程序,导出或者提取出关于类、属性、方法、参数等的详细信息,包括注释。这种动态获取信息以及动态调用对象方法的功能,被称为反射API。

#文章
https://www.php.net/manual/zh/book.reflection.php

class A { 

	public $ab =1;
	private $bc =2;

	public static $instance = null;

	public function abc() { 
		return "abcd";
	}

	public function abcd($a,$b) { 
		return $a."|".$b;
	}

	// 
	public function mf($type = 1) { 
		return "type:".$type;
	}

	public function ddd() { 
		return "ddd";
	}

}
public function rel() { 
	$obj = new \A();
	##建立一个A类的反射类   是反射ReflectionClass
	$obje2 = new \ReflectionClass($obj);
	$instance = $obj2->newInstance(); //相当于实例化这个类
	//dump($instance);

	#获取类当中的所有方法
	$methods = $obj2->getMethods();

	#获取方法的注释 
	foreach($methods as $method){
		dump($method->getDocComment());
	}

	#类下的所有属性 
	$properties = $obj2->getProperties();

	//方法一 
	#echo $instance->abc(1,2);

	//方法二
	#$method = $obj2->getMethod("abcd");
	#echo $method->invokeArgs($instance,["mmm","4444m"]);

	//方法三 
	#$method = $obj2->getMethod("ddd");
	#echo $method->invoke($instance);


	#判断某个方法是私有方法和共有方法 
	$method = new \ReflectionMethod($obj,"ddd");
	if ($method->isPublic()) {
		echo "ddd方法是公共";
	}
}

#基于A类的反射类

	##建立一个A类的反射类   是反射ReflectionClass
	$obje2 = new \ReflectionClass($obj);
	$instance = $obj2->newInstance(); //相当于实例化这个类
	//dump($instance);

在这里插入图片描述

#获取类当中的所有方法
$methods = $obj2->getMethods();

#获取方法的注释 
foreach($methods as $method){
	dump($method->getDocComment());
}

在这里插入图片描述

#类下的所有属性 
	$properties = $obj2->getProperties();

在这里插入图片描述

在这里插入图片描述

var_dump($method->getParameters());
在这里插入图片描述

传入几个参数
var_dump($method->getNumberOfParameters())

方法一、不使用反射的写法

public function initialize(array $config = array(), $reset = TRUE)
{
    $reflection = new ReflectionClass($this);
...
    #遍历config,如果存在set_方法调用set方法,没有则直接赋值
    foreach ($config as $key => $v) {
        if (method_exists($this, 'set_' . $key)) {
            $this->{'set_' . $key}($v);
        }else{
            $this->$key =$v;
        }
    }

这种方法有个弊端:无法判断config数组的所有key是否合法,是否是类的成员属性

方法二 框架内的实现

反射在这里的体现的好处是:可以判断类是否存在属性(hasProperty),和判断类是否存在方法(hasMethod)。

public function initialize(array $config = array(), $reset = TRUE)
{
    $reflection = new ReflectionClass($this);
...
    foreach ($config as $key => &$value)
    {
        if ($key[0] !== '_' && $reflection->hasProperty($key))
        {
            if ($reflection->hasMethod('set_'.$key))
            {
                $this->{'set_'.$key}($value);
            }
            else
            {
                $this->$key = $value;
            }
        }
    }

容器类

在这里插入图片描述
Container.php

namespace di;
class Container { 

	/*存放容器的数据
		把我们实例化的类放到这里面去 $instances 
	*/
	public $instances = [];


	/*容器中的对象实例*/
	protected static $instance;


	private function __construct() { 

	}

	/*获取当前容器的实例  单利模式*/
	public static function getInstance() { 
		if (is_null(static::$instance)) { 
				#new static   都是new 一个对象
				static::$instance = new static;
		}

		return static::$instance;
	}

	public function set($key,$value) { 

		$this->instances[$key] = $value;
	}
	
	//反射机制 获取容器里面的实例
	public function get($key) { 
		if(!empty($this->instances[$key])) { 
			$key = $this->instances[$key];
		}
		$reflect = new \ReflectionClass($key);
		//获取类的构造函数 没有构造函数直接new 类就行了
		$c = $reflect->getConstructor();
		if(!$c) {
				return new $key;
		}
		
		//获取构造函数里面的参数  $obj
		$params = $c->getParameters();
		if(empty($params)) { 
			return new $key;
		}
		#反射机制 
		foreach($prams as $param) {
			#不加Car判断不到是对象形式
			$class = $params->getClass();
			if(!$class){
				 
			}else{
				#name 就是 di\Car
				$args[] = $this->get($class->name);
				#var_dump($class);exit;
			}
		}
			#再用反射
		 return $reflect->newInstanceArgs($args);
	}
}


构造函数里面有这个类,需要转换成对象,做个反射把它实例化
到时候再去实例化某些类的时候(例如Person),把数据传递给参数就可以了

var_dump($class);exit; 打印的是
在这里插入图片描述

Person类下构造方法,就会把Car $obj ,场景转换成实例,通过实例调用newInstanceArgs做成实例化,它的参数就传递过来了 —下面的代码

 return $reflect->newInstanceArgs($args)

Car.php

namespace di;
class Car { 
	public function pay() { 
		return 123;
	}
}

Person.php

namespace di;
class Person{ 
	#加Car 满足是对象形式   默认的是Car的类
	public function __construct(Car $obj , $a = 12){
		$this->obj = $obj;  #是123
		$this->a = $a;
	}
	public function buy() {
		#return $this->$obj->pay();
		return $this->a ."|".$this->obj->pay();
	}
}

在这里插入图片描述

index.php
这是应用层
把实例set进去,用的时候get

键名 =>person 对象和实例 new \di\Person()

public function personbuy() { 
	#\di\Container::getInstance()->set("person", new \di\Person());
	#\di\Container::getInstance()->set("car", new \di\Car());

	\di\Container::getInstance()->set("person", "\di\Person");
	\di\Container::getInstance()->set("car", "\di\Car");
	
	#这传的事类名字  person类的
	$obj = \di\Container::getInstance()->get('person');
	#var_dump($obj->buy(\di\Container::getInstance()->get("car")));
	dump($obj->buy());
}

use ArrayAccess; 数组一样使用对象

#接口

interface Countable { 
	public function count();
}

#SingwaCount.php

class SingwaCount implements Countable { 
	public function count() { 
		return 234;
	}
}

#index.php

public function acount() { 
	$obj = new \SingwaCount;
	echo count($obj);
}

在这里插入图片描述
在这里插入图片描述

获取容器里面实例分析

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
app 就是类名和标识

   public static function get($abstract, $vars = [], $newInstance = false)
    {	
    	#本类调用make方法 
        return static::getInstance()->make($abstract, $vars, $newInstance);
    }
 if (true === $vars) {
            // 总是创建新的实例化对象
            $newInstance = true;  #为true 每次都要实例化这个类 
            $vars        = [];
        }

#是反射
use ReflectionClass;

$this->instances 容器里面

/**
     * 创建类的实例
     * @access public
     * @param  string        $abstract       类名或者标识
     * @param  array|true    $vars           变量
     * @param  bool          $newInstance    是否每次创建新的实例
     * @return object
     */
    public function make($abstract, $vars = [], $newInstance = false)
    {
        if (true === $vars) {
            // 总是创建新的实例化对象
            $newInstance = true;
            $vars        = [];
        }

        #默认第一次值都是   $this->name里面是app
        $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
        //第二次 就成为 $abstract => think\App;

        #看$this->instances有没有这个key    第三次再走就会走到这,因为容器里面有this->instances[think\App]
        if (isset($this->instances[$abstract]) && !$newInstance) { 
            #有值在容器里面就返回 可能是实例或者可能是app或者其他的
            return $this->instances[$abstract];
        }
		
		#如果没有就走下面的逻辑创建,创建并放到容器在返回
        if (isset($this->bind[$abstract])) {
            #绑定  concrete => think\App
            $concrete = $this->bind[$abstract];

            #instanceof (1)判断一个对象是否是某个类的实例,(2)判断一个对象是否实现了某个接口。
            #如果是命名函数走这里或者闭包时候 
            if ($concrete instanceof Closure) {//闭包场景
                $object = $this->invokeFunction($concrete, $vars);
            } else {
                //$this->name['app'] = think\App;
                $this->name[$abstract] = $concrete;
                return $this->make($concrete, $vars, $newInstance);
            }
        } else {
            //返回对象,没有实例 
            $object = $this->invokeClass($abstract, $vars);
        }

        if (!$newInstance) {
            #是对象 $object    
            $this->instances[$abstract] = $object;
            // $this->instances[think\App] = object;

        }
        return $object;
    }
 /**
     * 调用反射执行类的实例化 支持依赖注入
     * @access public
     * @param  string    $class 类名
     * @param  array     $vars  参数
     * @return mixed
     */
    public function invokeClass($class, $vars = [])
    {
        try {
            //实现这个反射类
            $reflect = new ReflectionClass($class);

            //当中是否存在__make方法 
            if ($reflect->hasMethod('__make')) {
                    #反射这个类实现这个方法
                $method = new ReflectionMethod($class, '__make');

                if ($method->isPublic() && $method->isStatic()) {
                    $args = $this->bindParams($method, $vars);
                    #就实例化了
                    return $method->invokeArgs(null, $args);
                }
            }

            $constructor = $reflect->getConstructor();

            $args = $constructor ? $this->bindParams($constructor, $vars) : [];

            return $reflect->newInstanceArgs($args);

        } catch (ReflectionException $e) {
            throw new ClassNotFoundException('class not exists: ' . $class, $class);
        }
    }
 /**
     * 调用反射执行类的实例化 支持依赖注入
     * @access public
     * @param  string    $class 类名
     * @param  array     $vars  参数
     * @return mixed
     */
    public function invokeClass($class, $vars = [])
    {
        try {
            //实现这个反射类
            $reflect = new ReflectionClass($class);

            //当中是否存在__make方法 
            if ($reflect->hasMethod('__make')) {
                    #反射这个类实现这个方法
                $method = new ReflectionMethod($class, '__make');

                if ($method->isPublic() && $method->isStatic()) {
                    $args = $this->bindParams($method, $vars);
                    #就实例化了
                    return $method->invokeArgs(null, $args);
                }
            }

            $constructor = $reflect->getConstructor();

            $args = $constructor ? $this->bindParams($constructor, $vars) : [];
            #实例化这个类
            return $reflect->newInstanceArgs($args);

        } catch (ReflectionException $e) {
            throw new ClassNotFoundException('class not exists: ' . $class, $class);
        }
    }

在这里插入图片描述

在这里插入图片描述

门面模式Facade

门面为容器中的类提供了一个静态调用接口
相比于传统的静态方法调用,带来了更好的可测试性和扩展性

thinkphp/library/think/facade/Config.php

public function facade() { 
	dump(\think\facade\Config::get("app."));
}

thinkphp/library/think/facade/Config.php 根本没有get方法,包括父类Facade也没有get方法就会执行下面的方法

 //调用实际类的方法 也就说在应用层调用一个不存在静态方法的时候就会走到__callStatic
    public static function __callStatic($method, $params)
    {
        return call_user_func_array([static::createFacade(), $method], $params);
    }

在这里插入图片描述
在这里插入图片描述

打印出

在这里插入图片描述

如果加一个 abcd 方法

在这里插入图片描述
应用层在调用 输出就下面的
在这里插入图片描述

/**
     * 创建Facade实例
     * @static
     * @access protected
     * @param  string    $class          类名或标识
     * @param  array     $args           变量
     * @param  bool      $newInstance    是否每次创建新的实例
     * @return object
     */
    protected static function createFacade($class = '', $args = [], $newInstance = false)
    {
        $class = $class ?: static::class;

        $facadeClass = static::getFacadeClass();

        if ($facadeClass) {
            $class = $facadeClass;
            #否则看上层self::$bind的属性,如果有就把值付给它$class
        } elseif (isset(self::$bind[$class])) {
            $class = self::$bind[$class];
        }

        if (static::$alwaysNewInstance) {
            $newInstance = true;
        }

        #容器拿到我们想要的这么一个类,比如容器有,我就返回容器里面的实例,容器理由config实例我就返回,如果没有走容器里的创建的实例逻辑
        return Container::getInstance()->make($class, $args, $newInstance);
    }

好处:我们能够通过静态的方式去调用类的一些方法

在这里插入图片描述

在这里插入图片描述
facade\Singwa.php

namespace app\facade;
use think\Facade;

class Singwa extends Facade { 

	 /**
     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
     * @access protected
     * @return string
     */
     protected static function getFacadeClass(){ 

     	return "singwa";#没有绑定Singwa
     	return '\app\common\Singwa';
     }
}

application\common\Singwa.php

namespace app\common;
class Singwa {
	public function abcd() { 
		return "abcd";
	}
}

通过静态的方式去调用实际方法里面的内容 门面模式的思想

public function facade() { 

	// 实际的 singwa调用
	#$obj = new \app\common\Singwa();
	#echo $obj->abcd();

	//需要把上面的内容转换为门面模式思想来调用
	echo \app\facade\Singwa::abcd();
}

在这里插入图片描述

第二种 如何绑定呢

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public function facade(){

	\think\Faade::bind('app\facade\Sing','app\common\Sing');
	dump(\app\facade\Sing::abcd());
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
本人在教Java方向课程时,发现现组长们不能象以前班级的学长写出封装得比较好的DAO工具类。于是,使用Facade模式和反射技术模拟Hibernate框架技术演示怎样书封装数据库的操作。 环境:Windows XP Professional, JDK 1.6, Eclipse 3.3 Europa, SQL Server 2000 使用步骤: 1. 下载压之后,使用Eclipse导入工程 2. 打开SQL server 2000的查询分析器 3. 把工程中的demospl.sql放到查询分析器中 4. 编辑com.jb.arklis.dao.MyDBConnection中的数据库名 5. 运行SQL脚本生成演示应用所需的数据库表与测试数据 6. 运行com.jb.arklis.app.DemoApplication类 7. 如果一切正常,那么可以看一个弹出的对话框,里面数据库表userinfo的十二记录 设计思路: 该自定的框架对书写客户端代码的人员只有两个要求: 1. 清楚数据库的表userinfo结构--有多少个字段和类型 2. 根据表可以书写一个对应的POJO类 如果以上两个条件成立,那么书写操作数据库的代码很简单:只需呼叫FacadeForDAO.findModel(表名)方法,在表名参数给出实际数据库中表的名称就可以返回一个包含对应POJO类的对象的集合--非常简单--输入表名得到Java对象的集合,然后根据需要使用这些对象。 给客户端代码方式是通过Facade对象封装DAO类,以及业务逻辑接口来实现的。对于数据库表与POJO类的映射是使用反射技术来完成。metainform部分使用.properties文档来对应Hibernate的xml影射档。我偷懒没有把jdbc部分和POJO的setter方法名写到.properties文档中去,jdbc部分使用com.jb.arklis.dao.MyDBConnection类来代理完成。 所以,重点看com.jb.arklis.dao.BaseDAO类中的public List findAll(String tableName)方法。该类注释非常详细,一行注释一行代码。如果还看不懂,那么你可以把工程中的“BaseDAO.未完全使用反向的写法”文档替换原来的BaseDAO试看看,我想如果替换之后,你一定能看懂反射的代码了。如果还看不懂????那我也没有办法了^_^ 阅读对象:所有立志学习Java技术的学员。 God bless you!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟伟哦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值