【问题标题】:PHP Decorator Writer ScriptPHP 装饰器编写器脚本
【发布时间】:2011-04-20 22:26:32
【问题描述】:

这些天我开始更频繁地在 PHP 中使用装饰器来修改对象在运行时的行为。我的问题主要是懒惰,我们有许多带有大量方法的遗留类,并且必须为每个装饰器类重写/覆盖所有方法的想法让我感到难过。有谁知道可以为我编写这些装饰器的命令行实用程序?

或者也许有更好的方法来解决这个问题?

【问题讨论】:

  • 许多装饰器可能是一个设计缺陷,但我会把它作为遗留的东西。我不知道有任何 cli 实用程序,但使用 ReflectionClass 几乎可以为您提供所需的所有数据,让您只需循环遍历属性并以简单字符串输出 PHP。

标签: php oop decorator


【解决方案1】:

GOF 建议此模式中的所有类都派生自一个抽象组件类。您可以从现有类派生一个类,以在 DynamicComponent 中添加此功能以达到相同的效果。您从此类派生内部对象。此类是可以使用魔术方法来动态处理属性和路由消息的地方。您可能需要 __get()、_set()、_call() 和 __construct()。我使用带有受保护构造函数的工厂方法来模拟多重继承。工厂返回一个独立组件或一个包装组件(通常由集合构建器指示,例如从数据库获取数据的树构建器)。

包装功能发生在一个抽象类中,该抽象类也派生自公共组件类。

您在公共接口中提供每个方法的实现。这些覆盖函数同步内部项和外部项中的数据,并提供连接以将未处理的消息传递到内部项。实际上,此模式中涉及的每个类都会自动从父类接收基本的 __get() 和 __set() 函数,并从 inner_item 接收扩展函数。这些在抽象类中链接以将消息发送到它们的 inner_item 对应项。 具体包装器从其父级免费获取组件接口,并且可以专注于添加的功能。如果您想摆脱公共父级,则需要在每个具体包装类中重新实现公共接口。另一个好处是能够将 compare 和 __toString() 等函数添加到基类中。这些对象可以在 usort() 和其他 list/tree/stack/cue/array/whatever 结构等函数中完全互换使用,因为它们不仅看起来像同一个接口,而且它们是同一种类型!

【讨论】:

    【解决方案2】:

    从问题中我了解到您懒得添加 other 方法,例如那些不修改装饰实例的。为此,您可以使用魔术方法__call

    public function __call($method, $args) {
        return call_user_func_array(
            array($this->decoratedInstance, $method),
            $args
        );
    }
    

    您还可以根据需要添加__callStatic__get__set。但请注意,魔术拦截器总是会导致一些性能损失。如果你有很多嵌套的装饰器,这可能会很明显。如有疑问,请进行基准测试。

    【讨论】:

    • 嘿 Gordon,是的,你是对的,这是我没有修改的那些,最好避免写作。谢谢。我已经尝试过这些方面的东西,问题是由于装饰器是你正在装饰的任何类的子类,被调用的方法已经存在(即它存在于父类中)所以 __call() 没有得到执行。
    • @owise1 那真是个奇怪的decorator。但是无论如何,如果该类是子类,我看不出问题吗?只需使用您选择的 IDE 并覆盖您需要覆盖的内容。其他一切都可以保持原样,因为它将从父类调用。
    • 感谢 Gordon,我认为您帮助我看到了我的方式中的错误。装饰器不一定是主类的子类。出于类型检查的原因,我的印象就是您这样做的方式(装饰对象仍然“看起来像”原始对象)。但是对接口的编程是完美的,所以我可以使用 __call()。
    • 顺便说一句,子类化导致了问题。当包含所有正确实例变量的原始对象被修饰,并且调用了一个未被覆盖并因此传递给父类的方法时,该父类没有返回正确值所需的实例变量。但没关系,那只是因为我一开始就做错了,谢谢@Gordon!
    猜你喜欢
    • 2021-01-28
    • 2012-10-28
    • 1970-01-01
    • 2011-03-19
    • 1970-01-01
    • 2012-11-05
    • 2015-03-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多