太空大逃杀无限金币钻石免广告
60.51MB · 2025-11-06
$logger = new Logger(); // 手动创建日志服务
$logger->log('用户登录成功'); // 调用日志方法
$cache = new Cache(); // 手动创建缓存服务
$cache->set('user:1001', $user); // 调用缓存方法
// 门面简化后的写法
Log::log('用户登录成功');
Cache::set('user:1001', $user);
区别:
new 对象new 对象Log::log($message) 的全过程__callStatic// 用户调用
Log::log($message);
Log 是 Facade 的子类static::__callStatic('log', [$message])__callStatic 的核心逻辑abstract class Facade {
public static function __callStatic($method, $args) {
// 1. 获取真实类名(Logger)
$instance = static::resolveFacadeInstance();
// 2. 调用真实实例的方法
return $instance->$method(...$args);
}
}
resolveFacadeInstance() 的实现protected static function resolveFacadeInstance() {
$serviceId = static::getFacadeClass(); // 'Logger'
return Container::getInstance()->make($serviceId);
}
getFacadeClass() 返回真实类名class Log extends Facade {
protected static function getFacadeClass() {
return Logger::class; // 返回真实类名
}
}
make() 创建实例$logger = Container::make(Logger::class);
$logger->log($message);
最终流程:
Log::log($message)
→ __callStatic('log', [...])
→ getFacadeClass() 返回 Logger::class
→ 容器 make(Logger::class)
→ 调用 $logger->log($message)
class Container {
protected static $instance;
public static function getInstance() {
if (is_null(static::$instance)) {
static::$instance = new self(); // 第一次 new
}
return static::$instance; // 后续直接返回
}
}
class OrderController {
public function __construct() {
// 错误:直接调用门面(耦合严重)
$this->logger = Log::class; // 这里赋值的是字符串 'Logger',不是对象
}
}
为什么错误?
赋值错误:Log::class 返回的是字符串 'Logger',不是 Logger 实例
var_dump(Log::class); // 输出: string(7) "Logger"
$this->logger 是字符串,不是对象$this->logger->log(...) 会报错:Trying to get property 'log' of non-object违反依赖注入原则:
LoggerInterface),而不是具体实现可测试性差:
// 1. 定义接口
interface LoggerInterface {
public function log(string $message);
}
// 2. 实现类
class FileLogger implements LoggerInterface {
public function log(string $message) {
file_put_contents('app.log', $message . PHP_EOL, FILE_APPEND);
}
}
// 3. 门面类
class Log extends Facade {
protected static function getFacadeClass() {
return FileLogger::class; // 返回真实类名
}
}
// 4. 控制器(正确写法)
class OrderController {
protected $logger;
// 正确:通过构造函数注入接口
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
public function createOrder() {
$this->logger->log('订单创建成功'); // 调用接口方法
}
}
为什么正确?
控制器依赖于 LoggerInterface(抽象),而非具体实现
测试时可轻松替换:
// 测试时使用 Mock
$mockLogger = $this->createMock(LoggerInterface::class);
$controller = new OrderController($mockLogger);
与容器解耦:容器负责创建 FileLogger 实例,控制器只需接收接口
全局服务:如日志、缓存、数据库连接
// 日志门面示例
Log::log('用户登录成功'); // 背后调用 $container->get(Logger::class)->log()
复杂组件:如支付网关、邮件服务
// 支付门面示例
Payment::pay($order); // 背后调用 $container->get(PaymentService::class)->pay()
| 场景 | 门面使用 | 正确做法 |
|---|---|---|
| 日志记录 | Log::info(...) | 在方法中调用(不作为依赖) 不要在构造函数中使用 |
| 控制器依赖 | Log::class | 构造函数注入 LoggerInterface |
| 业务逻辑 | Payment::pay(...) | 业务逻辑应通过服务类处理 不应直接在控制器中调用 |
| 场景 | 推荐方式 | 错误方式 |
|---|---|---|
| 日志记录 | Log::info(...) | $this->logger = Log::class |
| 缓存操作 | Cache::get(...) | $this->cache = Cache::class |
| 控制器依赖 | public function __construct(LoggerInterface $logger) | Log::class |
| 业务逻辑 | Payment::pay(...) | Payment::class |