geeks4change/composable-inheritance

通过魔法类加载器提供可组合继承。

1.0.x-dev 2022-04-28 13:02 UTC

This package is auto-updated.

Last update: 2024-08-29 22:12:16 UTC


README

问题所在

通过装饰来增强服务是一种常见的做法。然而,存在一大类类,虽然容易扩展,但实际上无法进行装饰。例如,具有受保护方法或/和紧密自我耦合(方法大量调用其他方法或使用受保护/私有状态)的类,增强一个方法通常会导致大量复制粘贴其他方法,或者复制内部状态,从根本上违背了代码重用的初衷。

我们可以继承这样的类,但这不是可组合的:在面向组件的架构中,组件A和组件B无法同时继承服务类而不相互紧密耦合。

一个示例用例

假设(例如在DreamHooks中),你想通过重写ModuleHandler::buildImplementationInfo来增强Drupal的ModuleHandler。不管怎样,它总是受保护的。但如果它是公共的,在一个装饰器中,我们不得不复制所有调用方法。

所以,我们可以通过symfony 服务装饰来子类化它并交换实现。但这很脆弱(不可组合):如果另一个模块也这样做,只有一个可以获胜。

解决方案

可组合继承通过在运行时创建扩展类来解决此问题,这样多个扩展就可以在不相互耦合的情况下继承相同的服务类。

如何使用

对于上述示例,我们使用Drupal的ServiceProvider机制来改变容器

use Drupal\dreamhooks\LegacyBridge\ModuleHandlerExtensionTrait';
use Drupal\dreamhooks\LegacyBridge\ModuleHandlerExtensionInterface';
use geeks4change\composable_inheritance\ComposableInheritance;

class DreamhooksServiceProvider extends ServiceProviderBase {

  public function alter(ContainerBuilder $container) {
    $definition = $container->getDefinition('module_handler');
    $class = $definition->getClass();
    // Or alternatively use ComposableInheritance::alter($class).
    $newClass = ComposableInheritance::create($class)
      ->useTrait(ModuleHandlerExtensionTrait::class)
      ->implementsInterface(ModuleHandlerExtensionInterface::class)
      ->class();
    $definition->setClass($newClass);
  }

}

然后,魔法类名将在运行时解析为扩展基类(无论其名称如何)的类,并使用特里特\Drupal\dreamhooks\LegacyBridge\ModuleHandlerExtensionTrait,并且还实现了接口\Drupal\dreamhooks\LegacyBridge\ModuleHandlerExtensionInterface

在纯Symfony中,你可以使用合适的编译器通过

Opcache

如果你想要为动态生成的类提供Opcache支持,设置一个名为COMPOSABLE_INHERITANCE_CLASS_STORAGE_DIRECTORY的环境变量到一个空目录(在webroot之外,以避免打开上传文件的远程代码执行攻击向量)。

注意事项

当然,如果逻辑不兼容,组合扩展或装饰可能会失败,并且可能可以通过优先级排序来修复。

可组合继承只能在一般意义上使子类扩展组合成为可能。