我想创建一个类型安全的通用日志记录decorator。
我有许多repositories(接口),需要一个装饰器来捕获它们可能抛出的异常,将它们传递给LoggerInterface的实例,然后重新抛出它们。可以为每个人创建一个专门的装饰器和测试,虽然这很麻烦(尤其是测试很好),而且我宁愿避免。
使用__call
创建更通用的装饰器是第一种想到的方法。但是,这会导致对象实例无法实现其装饰的存储库接口。这在我的项目中是不行的。有没有办法告诉PHP它确实实现了这个界面,比如使用了一些魔法reflection?
我已阅读" how to implement a decorator in PHP?"和" Best way to implement a decorator pattern for method result caching in PHP"这里是stackoverflow,它们概述了专用方法和通用方法,但两者都没有提供以类型安全方式执行通用方法的指示。自那些问题发布以来已经过了一段时间,所以可能情况发生了变化。我使用的是PHP 7,如果需要可以使用PHP 7.1。
PHPUnit_MockObject允许通过PHPUnit中熟悉的getMock
方法调用的相同代码构建实现接口的对象。这可以是通用装饰器的基础。但是,这需要在生产代码中使用模拟库。此外,这个库internally uses eval可以完成它的工作。这使我的项目失去了资格。
答案 0 :(得分:1)
另一种方法是在运行时动态创建修饰类。不幸的是PHP doesn't allow this out of the box。如果runkit扩展程序不是一个选项,您可以模拟Doctrine ORM的作用:DecoratorFactory
执行以下步骤:
请参阅http://www.doctrine-project.org/api/orm/2.4/source-class-Doctrine.ORM.Tools.EntityGenerator.html
上的示例答案 1 :(得分:0)
您将获得的唯一真正的方法是声明一个完全实现必要接口的基础Decorator类,然后根据它进行扩展。例如:
interface FooInterface {
function doFoo();
function doMoreFoo();
}
class Foo implements FooInterface {
public function __construct() {}
public function doFoo() {}
public function moreFoo() {}
}
class FooDecoratorBase implements FooInterface {
protected $foo;
public function __construct(FooInterface $foo) { $this->foo = $foo; }
public function doFoo() { $this->foo->doFoo(); }
public function moreFoo() { $this->foo->moreFoo(); }
}
class ExtraFooDecorator extends FooDecoratorBase {
public function extraFoo() {}
}
虽然我不能自己引用这些细节,但是其他PHP开发者告诉我,我认为在生产代码中使用Reflections是一个坏主意,因为它们的设计考虑了测试/调试,不是表现。
答案 2 :(得分:0)
“Generic TypeParameters”在PHP (except Facebook's HHVM variation)中不可用。所以这是不可能的。 answer of @Sammitch是你要走的路,如果你真的想走这条路。
但是,您正在尝试解决其他问题。
装饰器模式充当包装器。您可以使用它,如果您想为现有类添加其他功能,而不更改类本身(或它的对象状态)。
请考虑一下,你的装饰师将如何:
function DoSomeOperation() {
try {
return $this->decoratedObject->DoSomeOperation(); //Object is injected in Decorators Constructor
catch (\Exception $ex) {
Logger::Log($ex);
throw;
}
}
为什么不在存储库中没有装饰器的情况下进行简单操作:
function DoSomeOperation() {
try {
//Do The Logic
catch (\Exception $ex) {
Logger::Log($ex);
throw;
}
}
但也许PDO对象的装饰器/包装器,你真正想拥有的是什么? (ᵔᴥᵔ)