1. 将课堂代码全部上机操作至少一遍以上
单例模式:demo1.php
# 单例模式
namespace _1205;
use PDO;
class Temp{
}
$obj1 = new Temp();
$obj2 = new Temp();
var_dump($obj1, $obj2); # id不一样
echo '<br>';
var_dump($obj1===$obj2);
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(){
}
}
$obj1 = Demo1::getInstance();
$obj2 = Demo1::getInstance();
var_dump($obj1===$obj2);
echo '<br>';
var_dump($obj1, $obj2); # id一样
echo '<hr>';
# 单例模式应用场景
class Db{
# 将构造方法私有化, 只能在类内部调用
private function __construct(...$connectParams){
$dsn = $connectParams[0];
$name = $connectParams[1];
$pwd = $connectParams[2];
self::$pdo = new PDO($dsn, $name, $pwd);
}
# 目前只能在类内部将类实例化
# 当前类的实例
public static $pdo = null;
# 实例化当前类的方法
public static function getInstance(...$connectParams){
# 实例化当前类,并返回类实例/对象
# 判断是否已经被实例化: 若没有实例化,实例下; 若已实例化,直接返回它
if( is_null(self::$pdo) ){
# 构造函数不需要返回
new self(...$connectParams); # 不是self::$instance = new self(...$connectParams);
}
return self::$pdo;
}
# 禁用克隆方法
private function __clone(){
}
}
$connectParams = ['mysql:host=127.0.0.1;dbname=SqlTest;charset=utf8','root','root'];
$pdo = Db::getInstance(...$connectParams);
$sql = 'SELECT * FROM `zsgc` WHERE `id`>1 AND `id`<4;';
$data = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
echo '<pre>' .print_r( $data,true ). '</pre>';
工厂模式:demo2.php
# 工厂模式:批量创建类的实例/对象
namespace _1205;
class Demo2{
}
# file1
$obj = new Demo2();
# file2
$obj = new Demo2();
# file3
$obj = new Demo2();
class Test1{
public function __construct( $arg1 ){
echo '对象创建成功,参数是'.$arg1;
}
}
class Test2{
public function __construct( $arg1, $arg2 ){
echo '对象创建成功,参数是'.implode(', ',[$arg1, $arg2]);
}
}
class Test3{
public function __construct( $arg1, $arg2, $arg3){
echo '对象创建成功,参数是'.implode(', ',[$arg1, $arg2, $arg3]);
}
}
# 创建工厂类,专用于创建类实例
class factory{
public static function create($className, ...$argments ){
new $className(...$argments);
}
}
//echo Test1::class; # 完整类名,带命名空间
# 用工厂类创建类实例/对象
Factory::create( Test1::class, 100);
echo '<br>';
Factory::create( Test2::class, 100,200);
echo '<br>';
Factory::create( Test3::class, 100,200,300);
echo '<br>';
复习(依赖注入和服务容器)
1 依赖注入( 降低对象之间耦合 ):demo3.php
# 依赖注入:解决对象调用之间耦合
# 目标:将类方法中的new干掉
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 '开车去上班,呀呀呀';
}
}
# 实例化类
# 把工作类里new干掉 拎到外面注入,降低耦合
$car = new Car1();
$person = new Person1( $car );
echo $person->work();
echo '<hr>';
2.1 容器小案例之准备一:Product.php
namespace _1205;
class Product{
public function get( Maker $maker){
return '该手机由<span style="color:green;font-weight: bolder">'. $maker->get(). '</span>生产的';
}
}
2.2 容器小案例之准备二:Maker.php
# 制造商
namespace _1205;
class Maker{
public function get( ){
return '华为';
}
}
2.3 容器小案例之对比( 先不用容器 ):demo1.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();
2.4 容器小案例之准备三:Container.php
# 容器类
namespace _1205;
# 引用闭包别名
use \Closure;
class Contatiner{
# 类实例容器
protected $instance = [];
# 将类实例化的过程绑定到容器中
public function bind( $alias, Closure $process ){
$this->instance[$alias] = $process;
}
# 取出供养在容器中的实例化过程的闭包,并执行它
public function make($alias){
return $this->instance[$alias](); # 闭包,括号不能少
}
}
2.5 容器小案例之调用:demo2.php
# 使用容器Container.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 Contatiner();
# 将类实例化代码绑定到容器中
$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
namespace base\inc1;
class Car{
public function drive(){
return '开汽车';
}
}
工厂方法实例之准备二:Plane.php
namespace base\inc1;
class Plane{
public function drive(){
return '乘飞车';
}
}
工厂方法实例之准备三:Train.php
namespace base\inc1;
class Train{
public function drive(){
return '坐火车';
}
}
工厂方法实例之准备四:autoload.php
# 自动加载 是让命名空间的路径和文件所在路径一致
spl_autoload_register( function ($className){
# 将"\"替换成路径分隔符, 推荐使用常量:DIRECTORY_SEPARATOR, 而不是"/",可跨平台支持
$path = str_replace('\\', DIRECTORY_SEPARATOR, $className);
require dirname(__DIR__) . DIRECTORY_SEPARATOR . $path . '.php';
});
工厂方法实例之对比( 先使用传统方法 ):Travel1.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>';
工厂方法实例之调用:Travel2.php
# 旅行类: 使用工厂类(此时工作类依赖工厂类,而工厂类依赖3个外部类)
namespace base;
# 设置引用外部类名的别名
use base\inc1\Car;
use base\inc1\Train;
use base\inc1\Plane;
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('train'))->travelModel(), '<br>';
echo (new Travel2('plane'))->travelModel(), '<br>';
接口实例之准备一:iVehicle.php
namespace base\inc2;
# 交通工具的接口
interface iVehicle{
public function drive();
}
接口实例之准备二:接口子类 Car.php ( Plane.php / Train.php同理 )
namespace base\inc2;
class Car implements iVehicle {
public function drive(){
return '开汽车';
}
}
接口实例之调用:Travel3.php
# 旅行类: 使用一个接口 (此时工作类仅依赖一个接口类)
# 3个外部类继承接口并实现接口方法,工作类注入的3个外部类实例此时都是接口实例,做到面向接口
# 好处:可以更方便的扩展代码,如新增ship类,只需继承接口并实现接口方法,工作类再注入此接口实例
namespace base;
# 设置引用外部类名的别名
use base\inc2\Car;
use base\inc2\Train;
use base\inc2\Plane;
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() . ' : 去闯荡';
}
}
# 客户端调用
echo (new Travel3( new Car() ))->travelModel(), '<br>';
echo (new Travel3( new Train() ))->travelModel(), '<br>';
echo (new Travel3( new Plane() ))->travelModel(), '<br>';
2. 手写课堂笔记:1205.md
总结:
- 1. 单例模式:创建类的唯一实例
- 2. 工厂模式:批量创建类实例
- 3. 依赖注入:在外部实例化,并将实例注入到类中,避免直接在类里实例化,降低耦合
- 4. 服务容器:将类的实例化过程与容器绑定,并通过容器类来执行,统一管理,而不是在外部各自随便实例化
- 5. 面向接口编程:先通过传统方法实现,缺点是工作类与外部类耦合性太高;然后通过工厂方法实例改进,虽降低了工作类与外部类的耦合,但工厂类依旧依赖外部类;最后通过面向接口的方法,使外部类实例都成了接口实例,达到了工作类只依赖接口类的效果,统一了实例化过程,后续扩展代码更加方便