接上文:秒杀系列02
这里列举一个例子,一个Db类(数据库操作),一个User类(业务处理),其中User需要调用Db类查询。
准备一个数据库
关闭防火墙,我们是本地虚拟机,可以不设防火墙,避免连接资源时的限制。
加一张users表,并添加一点数据。
重新生成一个docker镜像
因为我们之前的php环境的镜像,是不支持pdo_mysql扩展的,我们加上。
dockerfile如下:
FROM php:8.2-cli
RUN echo "nameserver 8.8.8.8" > /etc/resolv.conf
RUN apt-get update && apt-get install -y \
libcurl4-openssl-dev \
libssl-dev \
libzip-dev \
libbrotli-dev \
libpng-dev \
default-libmysqlclient-dev \
unzip && \
docker-php-ext-install zip pdo pdo_mysql
RUN pecl install swoole && \
docker-php-ext-enable swoole
RUN pecl install redis && \
docker-php-ext-enable redis
WORKDIR /var/www
构建为镜像
docker build -t php8.2-swoole-pdo:v1.0.0 .
// 运行
docker run -it --name seckill -p 8001:80 -v /www/wwwroot/learnProject/seckill/v1:/var/www/v1 php8.2-swoole-pdo:v1.0.0 bash
早期的封装方式 ~ 正向控制
在v1根目录下,新建一个test.php文件,内容如下:
<?php
class Db {
private $pdo;
public function __construct($dsn) {
try {
$this->pdo = new PDO($dsn);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo "mysql连接失败:" . $e->getMessage();
}
}
public function queryForRows($sql) {
try {
$stmt = $this->pdo->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo "查询失败:" . $e->getMessage();
}
}
}
class User {
private $db;
public function __construct($dsn)
{
$this->db = new Db($dsn); // 主动实例化Db
}
public function getAllUsers() : array {
return $this->db->queryForRows("select * from users");
}
}
$dsn = "mysql:host=192.168.0.101;dbname=seckill;charset=utf8;user=seckill;password=BBHyiFWiXrpxji5S;"; // 可以用 .env 文件
$user = new User($dsn);
var_dump($user->getAllUsers());
上面代码存在的问题:
- Db 和 User 耦合度太高,存在强关联。
- User 是老大,Db 是小弟,按理来说,它们应该是平级的,User关注于业务,Db关注于数据操作。
- 执行顺序是 User -> Db,而且是在 User 的里面执行的 Db实例化,可以理解为是一种正向控制。
正确的做法:
不用在 User 中主动实例化 Db了,而是采用,向 User 注入 Db 的实例 。
现在的封装方式 ~ 依赖注入/控制反转
test.php
<?php
class Db {
private $pdo;
public function __construct($dsn) {
try {
$this->pdo = new PDO($dsn);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo "mysql连接失败:" . $e->getMessage();
}
}
public function queryForRows($sql) {
try {
$stmt = $this->pdo->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo "查询失败:" . $e->getMessage();
}
}
}
class User {
private $db;
public function __construct(Db $Db) // 这里直接注入Db的实例了
{
$this->db = $Db; // 这里不在主动实例化Db了,直接用注入的Db实例
}
public function getAllUsers() : array {
return $this->db->queryForRows("select * from users");
}
}
// 执行的顺序发生了改变
// User --> Db
// Db -> User 控制顺序发生了反转 -> 控制反转
$dsn = "mysql:host=192.168.0.101;dbname=seckill;charset=utf8;user=seckill;password=BBHyiFWiXrpxji5S;"; // 可以用 .env 文件
$db = new Db($dsn); // 现在和 User 地位平等了
$user = new User($db); // 依赖注入
var_dump($user->getAllUsers());
可以看到,经过上面的封装,Db 和 User 解除了强绑定,变成平级了,User 不再主动实例Db了,而是被动等待 Db依赖的注入,而且执行顺序也发生了改变,变为了 Db -> User,也叫控制反转。
依赖注入 Dependency Injection (DI)
控制反转 Inversion of Control (IoC)