一、设计模式
1. 单例模式
创建类的唯一实例
<?php
//定义命名空间
namespace _1205;
// 引入PDO类别名
use PDO;
class Temp
{
}
$obj1 = new Temp();
$obj2 = new Temp();
// 打印数据类型和值
var_dump($obj1, $obj2);
echo '<br>';
// 判断类实例化的两个对象是否完全相等
var_dump($obj1 === $obj2); // false
echo '<hr>';
// 单例模式:创建类的唯一实例
class Demo1
{
// 将构造方法私有化,这样就无法在类的外部实例化该类了
private function __construct()
{
}
// 目前,只能在类的内部将类实例化
// 由于不能通过创建对象实例化类,所以只能通过静态成员
// 添加一个静态属性作为类的实例
public static $instance = null;
// 实例化当前类的静态方法
public static function getInstance()
{
// 实例化当前类,并返回类实例/对象
// 首先判断当前类是否已经被实例化过了,如果没有的话就实例化,如果已实例化,就返回它
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
// 禁用克隆魔术方法
private function __clone()
{
}
}
//new Demo1(); // 报错,在外部无法创建类对象/实例
// 只能通过类名来进行实例化
$obj1 = Demo1::getInstance();
$obj2 = Demo1::getInstance();
var_dump($obj1 === $obj2); // true
echo '<br>';
var_dump($obj1, $obj2);
echo '<hr>';
// 单例模式应用场景
class Db
{
// 目前,只能在类内部将类实例化
// 当前类的实例
public static $pdo = null;
// 构造方法私有化
// 用剩余参数可以使参数以数组传入
private function __construct(...$connectParams)
{
$dsn = $connectParams[0];
$username = $connectParams[1];
$password = $connectParams[2];
self::$pdo = new PDO($dsn, $username, $password);
}
// 实例化当前类的方法
public static function getInstance(...$connectParams)
{
// 实例化当前类,并返回类实例/对象
// 首先判断当前类是否已经被实例化过了,如果没有的话就实例化,如果已实例化,就返回它
if (is_null(self::$pdo)) {
// 由于在构造方法中$pdo已经初始化了,所以不需要再对$pdo赋值了
new self(...$connectParams);
}
return self::$pdo;
}
// 禁用克隆魔术方法
private function __clone()
{
}
}
$connectParams = ['mysql:host=localhost;dbname=test','root','root'];
$pdo = Db::getInstance(...$connectParams);
// 通过PDO::query 执行SQL语句,返回结果集
echo '<pre>' . print_r($pdo->query('SELECT * FROM `computer`')->fetchAll(),true) . '</pre>';
2. 工厂模式
批量创建类实例/对象
<?php
// 定义命名空间
namespace _1205;
// 工厂模式:用于批量创建类的实例/对象
class Demo2
{
}
// 举例:在三个文件中,创建类实例
// file1.php
$obj = new Demo2();
// file2.php
$obj = new Demo2();
// file3.php
$obj = new Demo2();
class Test1
{
public function __construct($arg1)
{
echo '对象创建成功,参数是:' . $arg1;
}
}
class Test2
{
public function __construct($arg1, $arg2)
{
// implode 将索引数组元素组合成字符串
echo '对象创建成功,参数是:' . implode(',', [$arg1, $arg2]);
}
}
class Test3
{
public function __construct($arg1, $arg2, $arg3)
{
echo '对象创建成功,参数是:' . implode(',', [$arg1, $arg2, $arg3]);
}
}
// 工厂类:专用于创建类实例
class Factory
{
// $className:类名 ...$argments:剩余参数,代表实例类需要的参数
public static function create($className, ...$argments)
{
return new $className(...$argments);
}
}
//Test1::class 代表该类的完整类名,即 _1205\Test1:完整类名是带有命名空间的
// 用工厂类来创建类实例/对象
Factory::create(Test1::class, 100);
echo '<br>';
Factory::create(Test2::class, 100, 200);
echo '<br>';
Factory::create(Test3::class, 100, 200, 300);
二、依赖注入
<?php
// 定义命名空间
namespace _1205;
// 依赖注入:解决对象调用之间耦合
// 工作类
class Person
{
// 要依赖的外部对象
private $car = null;
// 在构造方法中将依赖的外部对象实例化
public function __construct()
{
$this->car = new Car();
}
// 该方法用于外部对象执行外部类中的方法
public function work()
{
return $this->car->drive();
}
}
// 依赖的外部类
class Car
{
public function drive()
{
return '开车去上班';
}
}
// 实例化类
$person = new Person();
echo $person->work();
echo '<hr>';
class Person1
{
// 要依赖的外部对象
private $car = null;
// 在构造方法中将依赖的外部对象实例化
// 注入点放到构造方法中
public function __construct(Car1 $car)
{
$this->car = $car;
}
// 该方法用于外部对象执行外部类中的方法
public function work()
{
return $this->car->drive();
}
}
// 依赖的外部类
class Car1
{
public function drive()
{
return '我要开车去上班';
}
}
// 实例化类
$car = new Car1();
$person = new Person1($car);
echo $person->work();
三、服务容器
制造商类 Maker.php
<?php
// 定义命名空间
namespace _1205;
// 制造商类
class Maker
{
public function get()
{
return '华为';
}
}
商品类 Product.php
<?php
// 定义命名空间
namespace _1205;
// 商品类
class Product
{
public function get(Maker $maker)
{
return '该手机是由:<span style="color: green;font-weight: bolder">' . $maker->get() . '</span>生产的';
}
}
先不用容器来实现
<?php
// 定义命名空间
namespace _1205;
// 引入商品类
require 'Product.php';
// 引入制造商类
require 'Maker.php';
// 先不用容器,将制造商类注入到产品类中
class Client1
{
// 输出商品与制造商
public function show()
{
// 创建产品的实例
$product = new Product();
// 创建制造商的实例
$maker = new Maker();
// 制造商注入到产品类中
return $product->get($maker);
}
}
// 客户端调用
echo (new Client1())->show();
容器类 Container.php
<?php
// 定义命名空间
namespace _1205;
// 引入闭包别名
use Closure;
// 容器类
class Container
{
// 通过该属性来存放类实例,即类实例容器
protected $instance = [];
// 将类实例化的过程绑定到容器中
public function bind($alias, Closure $process)
{
$this->instance[$alias] = $process;
}
// 将保存在容器中的实例化过程的闭包取出来,并执行它
public function make($alias)
{
return $this->instance[$alias]();
// return call_user_func_array($this->instance[$alias],[]);
}
}
用容器类来实现
<?php
// 定义命名空间
namespace _1205;
// 引入生产类
require 'Product.php';
// 引入制造商类
require 'Maker.php';
// 引入服务容器类
require 'Container.php';
class Client2
{
// 输出商品与制造商
public function show(Product $product,Maker $maker)
{
// 将制造商注入到产品类中
return $product->get($maker);
}
}
// 客户端调用
// 将实例绑定到容器中并实例化且返回
// 创建容器类实例/对象
$container = new Container();
// 将实例化代码绑定到容器中
$container->bind('product',function (){return new Product();});
$container->bind('maker',function (){return new Maker();});
// 创建类实例并返回
$product = $container->make('product');
$maker = $container->make('maker');
// 客户端调用
echo (new Client2())->show($product, $maker);
四、工厂方法实例
汽车类 Car.php
<?php
// 定义命名空间
namespace base\inc1;
// 汽车类
class Car
{
public function drive()
{
return '开汽车';
}
}
飞机类 Plane.php
<?php
// 定义命名空间
namespace base\inc1;
// 飞机类
class Plane
{
public function drive()
{
return '乘飞机';
}
}
火车类 Train.php
<?php
// 定义命名空间
namespace base\inc1;
// 火车类
class Train
{
public function drive()
{
return '坐火车';
}
}
自动加载 autoload.php
<?php
// 自动加载类文件
spl_autoload_register(function ($className){
$path = str_replace('\\','/',$className);
require dirname(__DIR__) . DIRECTORY_SEPARATOR . $path . '.php';
});
传统方式实现
<?php
// 定义命名空间
namespace base;
// 设置引用的外部类名的别名
use base\inc1\Car;
use base\inc1\Train;
use base\inc1\Plane;
// 引入自动加载类文件
require __DIR__ . '/autoload.php';
// 旅行类:使用最传统的方式
class Travel1
{
// 交通工具
private $vehicle;
// 构造方法,实现用于判断要实例化哪个类
public function __construct($vehicle)
{
switch (strtolower($vehicle)) { // 将参数值全部转为小写
case 'car':
$this->vehicle = new Car();
break;
case 'train':
$this->vehicle = new Train();
break;
case 'plane':
$this->vehicle = new Plane();
}
}
// 调用外部的一个依赖对象,实现其方法
public function travelModel()
{
return $this->vehicle->drive() . '去旅行';
}
}
// 客户端调用
echo (new Travel1('car'))->travelModel(), '<br>';
echo (new Travel1('train'))->travelModel(), '<br>';
echo (new Travel1('plane'))->travelModel(), '<br>';
通过工厂方法实现
<?php
// 定义命名空间
namespace base;
// 设置引用的外部类名的别名
use base\inc1\Car;
use base\inc1\Plane;
use base\inc1\Train;
// 自动加载
require __DIR__ . '/autoload.php';
// 工厂类,专用于创建类实例
class Factory
{
// 该类属性用于表示要创建的类的实例
protected static $instance = null;
// 判断要创建哪个类的实例
public static function getInstance($vehicle)
{
switch (strtolower($vehicle)){
case 'car':
self::$instance = new Car();
break;
case 'train':
self::$instance = new Train();
break;
case 'plane':
self::$instance = new Plane();
}
// 返回当前具体的交通工具
return self::$instance;
}
}
class Travel2
{
// 表示交通工具
private $vehicle;
// 构造方法
public function __construct($vehicle)
{
// 调用工厂类的方法创建类实例
$this->vehicle = Factory::getInstance($vehicle);
}
// 调用外部的一个依赖对象
public function travelModel()
{
return $this->vehicle->drive() . '和她一块去旅行';
}
}
// 客户端调用
echo (new Travel2('car'))->travelModel(), '<br>';
echo (new Travel2('plane'))->travelModel(), '<br>';
echo (new Travel2('train'))->travelModel(), '<br>';
五、面向接口编程
定义接口
<?php
// 定义命名空间
namespace base\inc2;
// 交通工具的接口
interface iVehicle
{
public function drive();
}
实现接口
<?php
// 定义命名空间
namespace base\inc2;
// 汽车类实现交通工具接口
class Car implements iVehicle
{
public function drive()
{
return '开汽车';
}
}
<?php
// 定义命名空间
namespace base\inc2;
// 飞机类实现交通工具接口
class Plane implements iVehicle
{
public function drive()
{
return '乘飞机';
}
}
<?php
// 定义命名空间
namespace base\inc2;
// 火车类实现交通工具类接口
class Train implements iVehicle
{
public function drive()
{
return '坐火车';
}
}
<?php
// 定义命名空间
namespace base\inc2;
// 轮船类实现交通工具接口
class Ship implements iVehicle
{
public function drive()
{
return '坐游轮';
}
}
面向接口编程
<?php
// 定义命名空间
namespace base;
// 设置引用的外部类的别名
use base\inc2\Car;
use base\inc2\Train;
use base\inc2\Plane;
use base\inc2\Ship;
use base\inc2\iVehicle;
// 引入自动加载
require __DIR__ . '/autoload.php';
// 旅行类
class Travel3
{
// 交通工具
private $vehicle;
// 构造方法 参数为实现接口的对象
public function __construct(iVehicle $vehicle)
{
$this->vehicle = $vehicle;
}
// 调用外部的一个依赖对象,实现其方法
public function travelModel()
{
return $this->vehicle->drive() . '-------去旅行';
}
}
// 客户端调用
$car = new Car();
echo (new Travel3($car))->travelModel(), '<br>';
echo (new Travel3(new Plane()))->travelModel(), '<br>';
echo (new Travel3(new Train()))->travelModel(), '<br>';
echo (new Travel3(new Ship()))->travelModel(), '<br>';
六、总结
单例模式:创建类的唯一实例
工厂模式:批量创建类实例,大大简化了创建类实例的过程
依赖注入就是将外部对象以参数的形式传入到普通方法或构造方法的参数中,从而实现注入到类中
服务容器就是将类的实例和实例化方法统一管理
面向接口编程可以统一类的实例化过程